[med-svn] [SCM] igv branch, master, updated. upstream/1.5.21-49-g6cfce68

Andreas Tille tille at debian.org
Sat Mar 12 23:26:34 UTC 2011


The following commit has been merged in the master branch:
commit 2321f628e95797bed4f6d5e3d349510d6292ab06
Author: Andreas Tille <tille at debian.org>
Date:   Sun Mar 13 00:21:43 2011 +0100

    Imported Upstream version 1.5.56

diff --git a/build.xml b/build.xml
index 0ff61e1..253fe5c 100644
--- a/build.xml
+++ b/build.xml
@@ -1,4 +1,22 @@
 
+<!--
+  ~ Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+  ~ All Rights Reserved.
+  ~
+  ~ This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+  ~ is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+  ~
+  ~ THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+  ~ ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+  ~ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+  ~ OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+  ~ RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+  ~ ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+  ~ DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+  ~ BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+  ~ FOREGOING.
+  -->
+
 <project name="IGV" default="all" basedir=".">
 
     <!-- Properties-->
@@ -8,14 +26,6 @@
     <property name="src.dir"       value="${basedir}/src/" />
     <property name="lib.dir"       value="${basedir}/lib/" />
 
-    <!-- Platform specific input files -->
-    <property name="native.dir"        value="${basedir}/native/" />
-    <property name="win.dir"           value="${native.dir}/win/" />
-    <property name="mac.intel.dir"     value="${native.dir}/mac-intel/" />
-    <property name="mac.powerpc.dir"   value="${native.dir}/mac-powerpc/" />
-    <property name="linux64.dir"       value="${native.dir}/linux-64/" />
-    <property name="linux.dir"       value="${native.dir}/linux/" />
-
     <!-- Temp directory.  Sources are compiled here prior to archiving -->
     <property name="tmp.dir"       value="${basedir}/tmp/" />
     <property name="resource.dir"  value="${tmp.dir}resources/" />
@@ -25,8 +35,9 @@
 
     <!-- build specific properties.  -->
     <property name="build-number"  value="0" />
-    <property name="version"       value="1.4.2" />
+    <property name="version"       value="${build-number}" />
     <property name="data-server-url"    value="http://www.broad.mit.edu/igv/resources/dataServerRegistry" />
+    <property name="genome-server-url"    value="http://igv.broadinstitute.org/genomes/genomes.txt" />
 
 
     <!-- Clean all generated artifacts -->
@@ -54,27 +65,38 @@
         <!-- Create class path for manifest -->
         <path id="@jars">
             <pathelement path="${lib.dir}AbsoluteLayout.jar" />
-            <pathelement path="${lib.dir}ATV-3.1.jar" />
-            <pathelement path="${lib.dir}colt.jar" />
+            <pathelement path="${lib.dir}batik-bridge.jar" />
+            <pathelement path="${lib.dir}batik-codec.jar" />
+            <pathelement path="${lib.dir}batik-awt-util.jar" />
+			<pathelement path="${lib.dir}batik-css.jar" />
+            <pathelement path="${lib.dir}batik-dom.jar" />
+            <pathelement path="${lib.dir}batik-ext.jar" />
+			<pathelement path="${lib.dir}batik-gui-util.jar" />
+			<pathelement path="${lib.dir}batik-gvt.jar" />
+			<pathelement path="${lib.dir}batik-parser.jar" />
+			<pathelement path="${lib.dir}batik-svg-dom.jar" />
+			<pathelement path="${lib.dir}batik-svggen.jar" />
+			<pathelement path="${lib.dir}batik-transcoder.jar" />
+            <pathelement path="${lib.dir}batik-util.jar" />
+            <pathelement path="${lib.dir}batik-xml.jar" /> 
             <pathelement path="${lib.dir}commons-math-1.1.jar" />
+			<pathelement path="${lib.dir}commons-net-2.0.jar" />
             <pathelement path="${lib.dir}concurrent.jar" />
             <pathelement path="${lib.dir}jargs.jar" />
-            <pathelement path="${lib.dir}jhdf5.jar" />
-            <pathelement path="${lib.dir}jhdf.jar" />
-            <pathelement path="${lib.dir}jibble.jar" />
             <pathelement path="${lib.dir}jide-action.jar" />
             <pathelement path="${lib.dir}jide-common.jar" />
             <pathelement path="${lib.dir}jide-components.jar" />
             <pathelement path="${lib.dir}jide-dialogs.jar" />
             <pathelement path="${lib.dir}jide-dock.jar" />
             <pathelement path="${lib.dir}jide-grids.jar" />
-            <pathelement path="${lib.dir}junit-4.5.jar" />
             <pathelement path="${lib.dir}jlfgr-1_0.jar" />
-            <pathelement path="${lib.dir}ledatastream.jar" />
+            <pathelement path="${lib.dir}junit-4.5.jar" />   
             <pathelement path="${lib.dir}log4j-1.2.15.jar" />
-            <pathelement path="${lib.dir}maf.jar" />
-            <pathelement path="${lib.dir}sam-1.08.jar" />
-            <pathelement path="${lib.dir}swing-layout-1.0.jar" />
+            <pathelement path="${lib.dir}sam-1.38.jar" />
+            <pathelement path="${lib.dir}swing-layout-1.0.jar" />           
+            <pathelement path="${lib.dir}xml-apis-ext-1.3.04" />
+            <pathelement path="${lib.dir}xml-apis-1.3.04.jar" />
+            <pathelement path="${lib.dir}Jama-1.0.2.jar" />
         </path>
 
         <pathconvert property="class-path" pathsep=" " dirsep="/" refid="@jars">
@@ -90,6 +112,7 @@
         <replace file="${resource.dir}about.properties" token="@BUILD" value="${build-number}"/>
         <replace file="${resource.dir}about.properties" token="@TIMESTAMP" value="${timestamp}"/>
         <replace file="${resource.dir}about.properties" token="@DEFAULT_MASTER_RESOURCE_URL" value="${data-server-url}"/>
+        <replace file="${resource.dir}about.properties" token="@DEFAULT_MASTER_GENOME_URL" value="${genome-server-url}"/>
 
     </target>
 
diff --git a/igv_linux-64.sh b/igv_linux-64.sh
index f1be004..2eb859c 100644
--- a/igv_linux-64.sh
+++ b/igv_linux-64.sh
@@ -1 +1 @@
-java -Xmx750m -Djava.library.path=./native/linux-64 -jar igv.jar
\ No newline at end of file
+java -Xmx1800m  -jar igv.jar
\ No newline at end of file
diff --git a/igv_linux.sh b/igv_linux.sh
index e0a323c..3eebf9f 100644
--- a/igv_linux.sh
+++ b/igv_linux.sh
@@ -1 +1 @@
-java -Xmx750m -Djava.library.path=./native/linux -jar igv.jar
\ No newline at end of file
+java -Xmx1200m  -jar igv.jar
\ No newline at end of file
diff --git a/igv_mac-intel.command b/igv_mac-intel.command
new file mode 100644
index 0000000..0da6ff9
--- /dev/null
+++ b/igv_mac-intel.command
@@ -0,0 +1,2 @@
+cd `dirname $0`
+java -Xmx1200m  -Dapple.awt.graphics.UseQuartz=false -jar igv.jar
diff --git a/igv_mac-intel.sh b/igv_mac-intel.sh
deleted file mode 100644
index 4d4a01c..0000000
--- a/igv_mac-intel.sh
+++ /dev/null
@@ -1 +0,0 @@
-java -Xmx750m -Djava.library.path=./native/mac-intel -Dapple.awt.graphics.UseQuartz=false -jar igv.jar
diff --git a/igv_mac-powerpc.sh b/igv_mac-powerpc.sh
index 3ed305d..b662be3 100644
--- a/igv_mac-powerpc.sh
+++ b/igv_mac-powerpc.sh
@@ -1 +1 @@
-java -Xmx750m -Djava.library.path=./native/mac-powerpc  -Dapple.awt.graphics.UseQuartz=false -jar igv.jar
+java -Xmx750m   -Dapple.awt.graphics.UseQuartz=false -jar igv.jar
diff --git a/igv_win.bat b/igv_win.bat
index ef91750..fcc5980 100644
--- a/igv_win.bat
+++ b/igv_win.bat
@@ -1 +1,4 @@
-java -Xmx750m -Djava.library.path=./native/win -Dsun.java2d.noddraw=true -jar igv.jar
\ No newline at end of file
+::Get the current batch file's short path
+for %%x in (%0) do set BatchPath=%%~dpsx
+for %%x in (%BatchPath%) do set BatchPath=%%~dpsx
+java -Xmx750m -Dsun.java2d.noddraw=true -jar %BatchPath%\igv.jar
\ No newline at end of file
diff --git a/lib/AbsoluteLayout.jar b/lib/AbsoluteLayout.jar
new file mode 100644
index 0000000..b51cba7
Binary files /dev/null and b/lib/AbsoluteLayout.jar differ
diff --git a/lib/jide-action.jar b/lib/jide-action.jar
new file mode 100644
index 0000000..3d4d035
Binary files /dev/null and b/lib/jide-action.jar differ
diff --git a/lib/jide-common.jar b/lib/jide-common.jar
new file mode 100644
index 0000000..55a4adf
Binary files /dev/null and b/lib/jide-common.jar differ
diff --git a/lib/jide-components.jar b/lib/jide-components.jar
new file mode 100644
index 0000000..f20c584
Binary files /dev/null and b/lib/jide-components.jar differ
diff --git a/lib/jide-dialogs.jar b/lib/jide-dialogs.jar
new file mode 100644
index 0000000..b49bc60
Binary files /dev/null and b/lib/jide-dialogs.jar differ
diff --git a/lib/jide-dock.jar b/lib/jide-dock.jar
new file mode 100644
index 0000000..88eeb1c
Binary files /dev/null and b/lib/jide-dock.jar differ
diff --git a/lib/jide-grids.jar b/lib/jide-grids.jar
new file mode 100644
index 0000000..e10ee82
Binary files /dev/null and b/lib/jide-grids.jar differ
diff --git a/lib/jlfgr-1_0.jar b/lib/jlfgr-1_0.jar
new file mode 100644
index 0000000..6fdf3d5
Binary files /dev/null and b/lib/jlfgr-1_0.jar differ
diff --git a/readme_src.txt b/readme_src.txt
index 4b1b8ab..93cf7b3 100644
--- a/readme_src.txt
+++ b/readme_src.txt
@@ -34,7 +34,7 @@ igv_win.bat         (for Windows systems)
 igv_linux.sh        (for LINUX 32 bit systems)
 igv_linux-64.sh     (for LINUX 64 bit systems)
 igv_mac-powerpc.sh  (for MAC OsX POWERPC systems)
-igv_mac-intel.sh    (for MAC OsX INTEL systems)
+igv_mac-intel.command    (for MAC OsX INTEL systems, double-click to start)
 
 The bat and shell scripts are configured to start IGV with 750MB of
 memory.  This is a reasonable default for most machines.  If you are
diff --git a/src/copyright.txt b/src/copyright.txt
index 2e10ac8..f491277 100644
--- a/src/copyright.txt
+++ b/src/copyright.txt
@@ -1,14 +1,18 @@
 /*
- * The Broad Institute
- * SOFTWARE COPYRIGHT NOTICE AGREEMENT
- * This is copyright (2007-2009) by the Broad Institute/Massachusetts Institute
- * of Technology.  It is licensed to You under the Gnu Public License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
  *
- *    http://www.opensource.org/licenses/gpl-2.0.php
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
  *
- * This software is supplied without any warranty or guaranteed support
- * whatsoever. Neither the Broad Institute nor MIT can be responsible for its
- * use, misuse, or functionality.
-*/
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+ 
diff --git a/src/demos/ColorBlocks.java b/src/demos/ColorBlocks.java
index 4ce577c..c4b161c 100644
--- a/src/demos/ColorBlocks.java
+++ b/src/demos/ColorBlocks.java
@@ -1,17 +1,20 @@
 /*
- * The Broad Institute
- * SOFTWARE COPYRIGHT NOTICE AGREEMENT
- * This is copyright (2007-2009) by the Broad Institute/Massachusetts Institute 
- * of Technology.  It is licensed to You under the Gnu Public License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
  *
- *    http://www.opensource.org/licenses/gpl-2.0.php
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
  *
- * This software is supplied without any warranty or guaranteed support
- * whatsoever. Neither the Broad Institute nor MIT can be responsible for its
- * use, misuse, or functionality.
-*/
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
 
 /*
  * ColorBlocks.java
diff --git a/src/demos/ColorComposite.java b/src/demos/ColorComposite.java
index 3fca9a6..f50b6f1 100644
--- a/src/demos/ColorComposite.java
+++ b/src/demos/ColorComposite.java
@@ -1,17 +1,20 @@
 /*
- * The Broad Institute
- * SOFTWARE COPYRIGHT NOTICE AGREEMENT
- * This is copyright (2007-2009) by the Broad Institute/Massachusetts Institute 
- * of Technology.  It is licensed to You under the Gnu Public License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
  *
- *    http://www.opensource.org/licenses/gpl-2.0.php
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
  *
- * This software is supplied without any warranty or guaranteed support
- * whatsoever. Neither the Broad Institute nor MIT can be responsible for its
- * use, misuse, or functionality.
-*/
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
 
 /*
  * ColorComposite.java
diff --git a/src/demos/GradientPaintDemo.java b/src/demos/GradientPaintDemo.java
index 7df7382..2f187dc 100644
--- a/src/demos/GradientPaintDemo.java
+++ b/src/demos/GradientPaintDemo.java
@@ -1,17 +1,20 @@
 /*
- * The Broad Institute
- * SOFTWARE COPYRIGHT NOTICE AGREEMENT
- * This is copyright (2007-2009) by the Broad Institute/Massachusetts Institute 
- * of Technology.  It is licensed to You under the Gnu Public License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
  *
- *    http://www.opensource.org/licenses/gpl-2.0.php
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
  *
- * This software is supplied without any warranty or guaranteed support
- * whatsoever. Neither the Broad Institute nor MIT can be responsible for its
- * use, misuse, or functionality.
-*/
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
 
 /*
  * GradientPaintDemo.java
diff --git a/src/demos/GradientPaintDemo2.java b/src/demos/GradientPaintDemo2.java
index e716c68..48ba814 100644
--- a/src/demos/GradientPaintDemo2.java
+++ b/src/demos/GradientPaintDemo2.java
@@ -1,17 +1,20 @@
 /*
- * The Broad Institute
- * SOFTWARE COPYRIGHT NOTICE AGREEMENT
- * This is copyright (2007-2009) by the Broad Institute/Massachusetts Institute 
- * of Technology.  It is licensed to You under the Gnu Public License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
  *
- *    http://www.opensource.org/licenses/gpl-2.0.php
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
  *
- * This software is supplied without any warranty or guaranteed support
- * whatsoever. Neither the Broad Institute nor MIT can be responsible for its
- * use, misuse, or functionality.
-*/
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
 
 /*
  * GradientDemo.java
diff --git a/src/demos/JArgsTest.java b/src/demos/JArgsTest.java
index 2fe6c62..c3d6cf2 100644
--- a/src/demos/JArgsTest.java
+++ b/src/demos/JArgsTest.java
@@ -1,4 +1,22 @@
 /*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
  * To change this template, choose Tools | Templates
  * and open the template in the editor.
  */
diff --git a/src/demos/Paints.java b/src/demos/Paints.java
index 115cfd3..6ea187c 100644
--- a/src/demos/Paints.java
+++ b/src/demos/Paints.java
@@ -1,17 +1,20 @@
 /*
- * The Broad Institute
- * SOFTWARE COPYRIGHT NOTICE AGREEMENT
- * This is copyright (2007-2009) by the Broad Institute/Massachusetts Institute 
- * of Technology.  It is licensed to You under the Gnu Public License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
  *
- *    http://www.opensource.org/licenses/gpl-2.0.php
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
  *
- * This software is supplied without any warranty or guaranteed support
- * whatsoever. Neither the Broad Institute nor MIT can be responsible for its
- * use, misuse, or functionality.
-*/
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
 
 /*
  * Paints.java
diff --git a/src/demos/RubberBand.java b/src/demos/RubberBand.java
index d69740e..6c6e13f 100644
--- a/src/demos/RubberBand.java
+++ b/src/demos/RubberBand.java
@@ -1,17 +1,20 @@
 /*
- * The Broad Institute
- * SOFTWARE COPYRIGHT NOTICE AGREEMENT
- * This is copyright (2007-2009) by the Broad Institute/Massachusetts Institute 
- * of Technology.  It is licensed to You under the Gnu Public License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
  *
- *    http://www.opensource.org/licenses/gpl-2.0.php
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
  *
- * This software is supplied without any warranty or guaranteed support
- * whatsoever. Neither the Broad Institute nor MIT can be responsible for its
- * use, misuse, or functionality.
-*/
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
 
 /*
  * RubberBand.java
diff --git a/src/demos/ScreenCapture.java b/src/demos/ScreenCapture.java
index ca45d98..8529ef6 100644
--- a/src/demos/ScreenCapture.java
+++ b/src/demos/ScreenCapture.java
@@ -1,17 +1,20 @@
 /*
- * The Broad Institute
- * SOFTWARE COPYRIGHT NOTICE AGREEMENT
- * This is copyright (2007-2009) by the Broad Institute/Massachusetts Institute 
- * of Technology.  It is licensed to You under the Gnu Public License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
  *
- *    http://www.opensource.org/licenses/gpl-2.0.php
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
  *
- * This software is supplied without any warranty or guaranteed support
- * whatsoever. Neither the Broad Institute nor MIT can be responsible for its
- * use, misuse, or functionality.
-*/
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
 
 /*
  * ScreenCapture.java
diff --git a/src/edu/mit/broad/picard/PicardException.java b/src/edu/mit/broad/picard/PicardException.java
index c2bf9af..7577dc1 100644
--- a/src/edu/mit/broad/picard/PicardException.java
+++ b/src/edu/mit/broad/picard/PicardException.java
@@ -1,25 +1,19 @@
 /*
- * The MIT License
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
  *
- * Copyright (c) 2009 The Broad Institute
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
  *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
  */
 package edu.mit.broad.picard;
 
diff --git a/src/edu/mit/broad/picard/illumina/SolexaQualityConverter.java b/src/edu/mit/broad/picard/illumina/SolexaQualityConverter.java
index 9842132..05cbbde 100644
--- a/src/edu/mit/broad/picard/illumina/SolexaQualityConverter.java
+++ b/src/edu/mit/broad/picard/illumina/SolexaQualityConverter.java
@@ -1,25 +1,19 @@
 /*
- * The MIT License
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
  *
- * Copyright (c) 2009 The Broad Institute
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
  *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
  */
 package edu.mit.broad.picard.illumina;
 
diff --git a/src/edu/mit/broad/picard/util/ArrayUtil.java b/src/edu/mit/broad/picard/util/ArrayUtil.java
index c97a87f..d142320 100644
--- a/src/edu/mit/broad/picard/util/ArrayUtil.java
+++ b/src/edu/mit/broad/picard/util/ArrayUtil.java
@@ -1,25 +1,19 @@
 /*
- * The MIT License
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
  *
- * Copyright (c) 2009 The Broad Institute
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
  *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
  */
 package edu.mit.broad.picard.util;
 
diff --git a/src/edu/mit/broad/picard/util/SequenceUtil.java b/src/edu/mit/broad/picard/util/SequenceUtil.java
index 1b1dc1c..fe1611a 100644
--- a/src/edu/mit/broad/picard/util/SequenceUtil.java
+++ b/src/edu/mit/broad/picard/util/SequenceUtil.java
@@ -1,25 +1,19 @@
 /*
- * The MIT License
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
  *
- * Copyright (c) 2009 The Broad Institute
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
  *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
  */
 package edu.mit.broad.picard.util;
 
@@ -127,11 +121,11 @@ public class SequenceUtil {
         for (int i = 0; i < s1.size(); ++i) {
             if (!s1.get(i).isSameSequence(s2.get(i))) {
                 String s1Attrs = "";
-                for (final java.util.Map.Entry<String, Object> entry : s1.get(i).getAttributes()) {
+                for (final java.util.Map.Entry<String, String> entry : s1.get(i).getAttributes()) {
                     s1Attrs += "/" + entry.getKey() + "=" + entry.getValue();
                 }
                 String s2Attrs = "";
-                for (final java.util.Map.Entry<String, Object> entry : s2.get(i).getAttributes()) {
+                for (final java.util.Map.Entry<String, String> entry : s2.get(i).getAttributes()) {
                     s2Attrs += "/" + entry.getKey() + "=" + entry.getValue();
                 }
                 throw new PicardException("Sequences at index " + i + " don't match: " +
diff --git a/src/edu/mit/broad/picard/util/StringUtil.java b/src/edu/mit/broad/picard/util/StringUtil.java
index 0fcbd2d..3a13169 100644
--- a/src/edu/mit/broad/picard/util/StringUtil.java
+++ b/src/edu/mit/broad/picard/util/StringUtil.java
@@ -1,25 +1,19 @@
 /*
- * The MIT License
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
  *
- * Copyright (c) 2009 The Broad Institute
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
  *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
  */
 package edu.mit.broad.picard.util;
 
diff --git a/src/edu/mit/broad/prodinfo/chromosome/BasicGenomicAnnotation.java b/src/edu/mit/broad/prodinfo/chromosome/BasicGenomicAnnotation.java
new file mode 100644
index 0000000..bae3eeb
--- /dev/null
+++ b/src/edu/mit/broad/prodinfo/chromosome/BasicGenomicAnnotation.java
@@ -0,0 +1,573 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package edu.mit.broad.prodinfo.chromosome;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Stack;
+
+//import edu.mit.broad.prodinfo.gene.GeneAnnotation;
+import edu.mit.broad.prodinfo.genomicplot.GenomicAnnotation;
+import edu.mit.broad.prodinfo.genomicplot.TwoSubjectAnnotation;
+import edu.mit.broad.prodinfo.sequence.Sequence;
+
+public class BasicGenomicAnnotation implements GenomicAnnotation {
+	private int start;
+	private int end;
+	private String name;
+	//private String displayName;
+	private String orientation;
+	private String chromosome;
+	private Sequence sequence;
+	private int fivePrimerBuffer;
+	private int threePrimerBuffer;
+	private String id;
+	private double score;
+	
+	//This is a hack to speed up sequencial search and operations that involve a list
+	int lastBreakInIndex = 0;
+	
+	protected BasicGenomicAnnotation() {
+		super();
+	}
+	public BasicGenomicAnnotation(String name) {
+		super();
+		this.name = name;
+		//this.displayName = name;
+	}
+	
+	public BasicGenomicAnnotation(String name, String chr, int start, int end) {
+		this(name);
+		setChromosome(chr);
+		setStart(start);
+		setEnd(end);
+	}
+	
+	public BasicGenomicAnnotation(GenomicAnnotation ga) {
+		start = ga.getStart();
+		end   = ga.getEnd();
+		name  = ga.getName();
+		orientation = ga.getOrientation();
+		score = ga.getScore();
+		chromosome = ga.getChromosome() != null ? ga.getChromosome().intern() : null;
+		sequence = ga.getSequence();
+		fivePrimerBuffer = ga.getFivePrimeBases();
+		threePrimerBuffer = ga.getThreePrimeBases();
+		id = ga.getId();
+	}
+	
+	/**
+	 * A basic genomic annotation may be built from a raw string array
+	 * where the first entry is the chromosome, the second the start and
+	 * the third the end.
+	 */
+	public BasicGenomicAnnotation(String [] info) {
+		super();
+		//? info[0] : info[0] + "_" + info[1] + "-" + info[2])
+		if(info[0].contains(":") ) {
+			String [] firstSplit = info[0].split(":");
+			setChromosome(firstSplit[0]);
+			String [] secondSplit = firstSplit[1].split("-");
+			setStart(Integer.parseInt(secondSplit[0]));
+			setEnd(Integer.parseInt(secondSplit[1]));
+		} else {
+			setChromosome(info[0].substring(3));
+			setStart(Integer.parseInt(info[1]));
+			setEnd(Integer.parseInt(info[2]));
+			if(info.length > 3) {
+				setId(info[3]);
+				setName(info[3]);
+			} else {
+				setName(info[0] + "_" + info[1] + "-" + info[2]);
+			}
+		}
+	}
+	
+	public boolean equals(Object o) {
+		if(o == null || !this.getClass().equals(o.getClass())) {
+			return false;
+		}
+		
+		BasicGenomicAnnotation other = (BasicGenomicAnnotation) o;
+		
+		if(chromosome != null && !chromosome.equals(other.getChromosome())) {
+			return false;
+		}
+		
+		if(name != null && !name.equals(other.getName())) {
+			return false;
+		}
+		
+		return start == other.getStart() && end == other.getEnd(); 
+	}
+	
+	public int hashCode() {
+		int hash = 0;
+
+		if(chromosome != null) {
+			char [] chrChars = chromosome.toCharArray();
+			for(int i = 0; i < chrChars.length; i++) {
+				hash += chrChars[i];
+			}
+			hash = hash << 31;
+		}
+		
+		if (name != null) {
+			hash += name.hashCode() << 30;
+		}
+		
+		hash += (start + end)/2;
+		return hash;
+	}
+	
+	public long getMiddle() {
+		return Math.round( (getStart() + getEnd())/(float)2);
+	}
+
+	public int getLength() { return getEnd() - getStart(); }
+	
+	public String toString() {
+		return getLocationString();
+	}
+	
+	public String getChromosomeString() {
+		if(getChromosome() == null) {
+			return null;
+		}
+
+		String str = "";
+		if(getChromosome().length() < 3 ) {
+			str = "chr";
+		}
+		
+		return str + getChromosome();
+	}
+	public String getLocationString() {
+		return getChromosomeString()+":"+getStart()+"-"+getEnd() ;
+	}
+	protected void setStart(String data) {
+		this.start = Integer.parseInt(data);
+	}
+	
+	public void setStart(int start) {
+		this.start = start;
+	}
+	
+	protected void setEnd(String data) {
+		this.end = Integer.parseInt(data);
+	}
+	
+	public void setEnd(int end) {
+		this.end = end;
+	}
+	
+	public int getStart() {
+		return start;
+	}
+
+	public int getEnd() {
+		return end;
+	}
+	
+	public double getScore() { return score;}
+	public void setScore(double score) { this.score = score;}
+	
+	public void setBoundariesFromAnnoations(List<? extends GenomicAnnotation> annotations) {
+		Iterator<? extends GenomicAnnotation> it = annotations.iterator();
+		while(it.hasNext()) {
+			GenomicAnnotation annot = it.next();
+			start = start > 0 ? Math.min(start, annot.getStart()) : annot.getStart();
+			end   = Math.max(end, annot.getEnd());
+		}
+	}
+
+	/**
+	 * Change the current instance to represent its intersection
+	 * with the provided annotation if they overlap
+	 * @param other
+	 */
+	public void takeIntersection(GenomicAnnotation other) {
+		if(getStart() < other.getEnd() && getEnd() > other.getStart()) {
+			setStart(Math.max(getStart(),other.getStart()));
+			setEnd(Math.min(getEnd(), other.getEnd()));
+		}
+	}
+	
+	/**
+	 * public void 
+	 */
+	
+	public int getDistanceTo(GenomicAnnotation other) {
+		int dist = 0;
+		if(!getChromosome().equals(other.getChromosome())) {
+			dist = 1000000000;
+		}else if(!overlaps(other)) {
+			if(getStart() > other.getEnd()) {
+				dist = getStart() - other.getEnd();
+			} else {
+				dist = other.getStart() - getEnd();
+			}
+		}
+		return dist;
+	}
+	
+	public String getId() {
+		return id;
+	}
+	public void setId(String id) {
+		this.id = id.intern();
+	}
+	
+	/**
+	 * Change the current instance to represent its union
+	 * with the provided annotation
+	 */
+	public void takeUnion(GenomicAnnotation other) {
+		if(overlaps(other)) {
+			setStart(Math.min(getStart(),other.getStart()));
+			setEnd(Math.max(getEnd(), other.getEnd()));
+		}
+	}
+	
+	/**
+	 * Change the current instance to represent the difference
+	 * of the instance with the other annotation
+	 * @param other
+	 */
+	public void reduceToDifference(GenomicAnnotation other) {
+		if(overlaps(other)) {
+			//System.out.println("Reduced annotation! " + this);
+			if(getStart() < other.getStart()) {
+				setEnd(Math.min(getEnd(), other.getStart() - 1));
+			} else {
+				setStart(Math.max(getStart(), other.getEnd() + 1));
+			}
+		}
+	}
+	
+	/**
+	 * Fragments the current annotation if it overlaps the provided one.
+	 * it returns an empty list if this annotation does not ovelap the
+	 * one passed in.
+	 * 
+	 * @param GenomicAnnotation annotation to disect the current one
+	 * @return List<GenomicAnnotation> of the disected annotations, an empty list is returned if 
+	 * 			the annotations do not overlap.
+	 */
+	public List<GenomicAnnotation> disect(GenomicAnnotation a) {
+		List<GenomicAnnotation> disection = new ArrayList<GenomicAnnotation>();
+		if(overlaps(a)) {
+			disection.addAll(this.minus(a));
+			//System.out.println("minus resulted in " + disection);
+			GenomicAnnotation copy = new BasicGenomicAnnotation(this);
+			copy.takeIntersection(a);
+			//System.out.println("and intersection in " + copy);
+			disection.add(copy);
+			
+			Collections.sort(disection);
+		}
+		//System.out.println("and disction is " + disection);
+		return disection;
+	}
+	
+	
+	/**
+	 * Fragments the current annotation if it overlaps the provided ones.
+	 * It returns a list with one component (this annotation) if no annotation 
+	 * in the provided list overlaps the discted annotaion.
+	 * 
+	 * @param List<GenomicAnnotation> <b>sorted</b> annotations with which to disect the current one.
+	 * @return List<GenomicAnnotation> of the disected annotations, a list just this annotation is returned if 
+	 * 			the annotations do not overlap.
+	 */
+	public List<GenomicAnnotation> disect(List<? extends GenomicAnnotation> disectors) {
+		List<GenomicAnnotation> disection = new ArrayList<GenomicAnnotation>();
+		disection.add(this);
+		Iterator<? extends GenomicAnnotation> annotIt = disectors.iterator();
+		while(annotIt.hasNext()) {
+			GenomicAnnotation a= annotIt.next();
+			if(a.getStart() > getEnd()) {
+				break;
+			}else if(!overlaps(a)) {
+				continue;
+			}
+			List<GenomicAnnotation> newDisection = new ArrayList<GenomicAnnotation>(disection.size());
+			Iterator<GenomicAnnotation> oldDisectionIt = disection.iterator();
+			while(oldDisectionIt.hasNext()) {
+				GenomicAnnotation disectionComponent = oldDisectionIt.next();
+				if(disectionComponent.overlaps(a)) {
+					newDisection.addAll(disectionComponent.disect(a));
+				} else {
+					newDisection.add(disectionComponent);
+				}
+			} 
+			disection = newDisection;
+		}
+		return disection;
+	}
+	
+	public void stitchTo(GenomicAnnotation other) {
+		setStart(Math.min(getStart(), other.getStart()));
+		setEnd(Math.max(getEnd(), other.getEnd()));
+	}
+	
+	public boolean isFlankedBy(TwoSubjectAnnotation twoSubjectAnnotation, int buffer) {
+		GenomicAnnotation left = new BasicGenomicAnnotation("left");
+		left.setStart(getStart() - buffer);
+		left.setEnd(getStart() + buffer);
+		
+		GenomicAnnotation right = new BasicGenomicAnnotation("right");
+		right.setStart(getEnd() - buffer);
+		right.setEnd(getEnd() + buffer);
+		
+		
+		GenomicAnnotation A = new BasicGenomicAnnotation("A");
+		A.setStart(twoSubjectAnnotation.getAStart());
+		A.setEnd(twoSubjectAnnotation.getAEnd());
+		
+		GenomicAnnotation B = new BasicGenomicAnnotation("B");
+		B.setStart(twoSubjectAnnotation.getBStart());
+		B.setEnd(twoSubjectAnnotation.getBEnd());
+		
+		return (A.overlaps(left) && B.overlaps(right)) || (A.overlaps(right) && B.overlaps(left));
+	}
+	
+	public List<GenomicAnnotation> minus(List<? extends GenomicAnnotation> others, boolean listIsDisjoint, int startAtIdx) {
+		ArrayList<GenomicAnnotation> dif = new ArrayList<GenomicAnnotation>();
+		dif.add(this);
+		Collections.sort(others);
+
+		Iterator<? extends GenomicAnnotation> it = null;
+		if(!listIsDisjoint) {
+			List<GenomicAnnotation> stitchedOthers = stitchList(others, 0);
+			it = stitchedOthers.iterator();
+		} else {
+			it = others.listIterator(startAtIdx);
+		}
+
+		lastBreakInIndex = startAtIdx;
+		while(it.hasNext()) {
+			GenomicAnnotation a = it.next();
+			if(a.getStart() > getEnd()) {
+				break;
+			}
+			lastBreakInIndex++;
+			ArrayList<GenomicAnnotation> newList = new ArrayList<GenomicAnnotation>(dif.size());
+			Iterator<GenomicAnnotation> oldListIt = dif.iterator();
+			while(oldListIt.hasNext()) {
+				GenomicAnnotation oldRegion = oldListIt.next();
+				newList.addAll(oldRegion.minus(a));
+			}
+			dif = newList;
+		}
+		
+		//Collections.sort(dif, new PositionComparator());
+		
+		return dif;
+	}
+	
+	public List<GenomicAnnotation> minus(List<? extends GenomicAnnotation> others) {
+		return minus(others, false, 0);
+	}
+	
+	public List<GenomicAnnotation> minus(GenomicAnnotation other) {
+		List<GenomicAnnotation> dif = new ArrayList<GenomicAnnotation>();
+		if(!overlaps(other)) {
+			dif.add(this);
+		} else {
+			GenomicAnnotation intersection = new BasicGenomicAnnotation(this);
+			intersection.takeIntersection(other);
+			GenomicAnnotation first = new BasicGenomicAnnotation(this);
+			first.setEnd(intersection.getStart() - 1);
+			if(first.getLength() > 0) {
+				dif.add(first);
+			}
+			GenomicAnnotation second = new BasicGenomicAnnotation(this);
+			second.setStart(intersection.getEnd() + 1);
+			if(second.getLength() > 0) {
+				dif.add(second);
+			}
+		}
+		return dif;
+	}
+	/**
+	 * 
+	 * @param sortedList
+	 * @param maxDistanceToStitch
+	 * @return
+	 */	
+	public static <T extends GenomicAnnotation> List<T> stitchList(List<? extends T> sortedList, int maxDistanceToStitch) {
+
+		Stack<T> result = new Stack<T>();
+
+		if(sortedList.size() == 0 ) {
+			return result;
+		}
+		
+		Iterator<? extends T> it = sortedList.iterator();
+		result.push(it.next());
+		while(it.hasNext()) {
+			T next = it.next();
+			T curr = result.pop();
+			if(curr.overlaps(next,maxDistanceToStitch)) {
+				curr.stitchTo(next);
+				curr.setName(curr.getName()+"-"+next.getName());
+				result.push(curr);
+			} else {
+				result.push(curr);
+				result.push(next);
+			}
+		}
+		
+		return result;
+	}
+	
+	/**
+	 * 
+	 * @param other GenomicAnnotation to check of overlap
+	 * @return true if the current instance overlaps with the other one.
+	 */
+	public boolean overlaps(GenomicAnnotation other) {
+		//System.out.print("Does this<"+getName()+"_"+getStart()+"-"+getEnd()+"> overlap <"+other.getName()+"_"+other.getStart()+"-"+other.getEnd()+">");
+		//System.out.println(" !<"+(getStart() < other.getEnd() && getEnd() > other.getStart())+">!");
+		return overlaps(other, 0);
+	}
+	
+	/**
+	 * 
+	 * @param other - other genomic annotation
+	 * @param buffer if the overlap is within buffer they will be considered overlapping 
+	 *        even if they do not overlap within their original boundaries.
+	 * @return true if they overlap in this extended definition
+	 */
+	public boolean overlaps(GenomicAnnotation other, int buffer) {
+
+		return ((other.getChromosome() == null) || other.getChromosome().equals(getChromosome())) && (getStart() < other.getEnd() + buffer) && (getEnd() > other.getStart() - buffer);
+	}
+	
+	public boolean contains(GenomicAnnotation other) {
+		//System.out.println("Do this annotation " + toString() + " contains " + other );
+		return ((other.getChromosome() == null) || other.getChromosome().equals(getChromosome())) && getStart() <= other.getStart() && getEnd() >= other.getEnd();
+	}
+
+	public String getName() {
+		return name;
+	}
+	
+	public void setName(String name) {
+		this.name = name;
+	}
+	
+	public int lastIdxWhereListSearchBroke() { return lastBreakInIndex;}
+	
+	public void setReversedOrientation(boolean isInReversedOrientation) {
+		this.orientation = isInReversedOrientation ? "-" : "=";
+	}
+
+	public boolean inReversedOrientation() {
+		return "-".equals(orientation);
+	}
+	public Sequence getSequence() {
+		return sequence;
+	}
+	public void setSequence(Sequence sequence) {
+		this.sequence = sequence;
+	}
+	/*
+	public String getDisplayName() {
+		return displayName;
+	}
+	public void setDisplayName(String displayName) {
+		this.displayName = displayName;
+	}
+	*/
+	public boolean isOrientation() {
+		return "+".equals(orientation);
+	}
+	public void setOrientation(boolean orientation) {
+		setOrientation(orientation ? "+" : "-");
+	}
+	public void setOrientation(String orientationString) {
+		this.orientation = orientationString.intern();
+	}
+	public String getChromosome() {
+		return chromosome;
+	}
+	public void setChromosome(String chromosome) {
+		if(chromosome != null) {
+			String chr = chromosome;
+			if(chromosome.startsWith("chr")) {
+				chr = chromosome.replace("chr", "");
+			}
+			this.chromosome = chr.intern();
+		}
+	}
+	public void setThreePrimeBuffer(int bufferSize) {
+		setEnd(getEnd() + bufferSize);
+		this.threePrimerBuffer = bufferSize;
+	}
+	
+	public void setFivePrimeBuffer(int bufferSize) {
+		setStart(getStart() - bufferSize);
+		this.fivePrimerBuffer = bufferSize;
+	}
+	
+	public int getFivePrimeBases() {
+		return fivePrimerBuffer;
+	}
+
+
+	public int getThreePrimeBases() {
+		return threePrimerBuffer;
+	}
+	
+	public String getOrientation() { return inReversedOrientation() ? "-" : "+";}
+	
+
+
+	/** 
+	 * Default compareTo method: If annotations are in the same chromosome
+	 * their start/end locations are compared otherwise their chromosomes are.
+	 */
+	public int compareTo(GenomicAnnotation arg0) {
+		if(getChromosome() != null && arg0.getChromosome() != null && !getChromosome().equals(arg0.getChromosome())) {
+			return getChromosome().compareTo(arg0.getChromosome()) * 1000000000;
+		}
+		return getStart() != arg0.getStart() ? getStart() - arg0.getStart() : getEnd() - arg0.getEnd();
+	}
+	public int getOrientedEnd() {
+		return inReversedOrientation() ? getStart() : getEnd();
+	}
+	public int getOrientedStart() {
+		// TODO Auto-generated method stub
+		return inReversedOrientation() ? getEnd() : getStart();
+	}
+	public void addBlock(String name, int start, int end) {
+		//Do nothing if does not support blocks, override if desired.
+	}
+	public List<? extends GenomicAnnotation> getBlocks() {
+		return null;
+	}
+	public boolean mayHaveBlocks() {
+		return false;
+	}
+
+}
diff --git a/src/edu/mit/broad/prodinfo/chromosome/BasicTwoSubjectAnnotation.java b/src/edu/mit/broad/prodinfo/chromosome/BasicTwoSubjectAnnotation.java
new file mode 100644
index 0000000..11e1058
--- /dev/null
+++ b/src/edu/mit/broad/prodinfo/chromosome/BasicTwoSubjectAnnotation.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package edu.mit.broad.prodinfo.chromosome;
+
+import edu.mit.broad.prodinfo.genomicplot.GenomicAnnotation;
+import edu.mit.broad.prodinfo.genomicplot.TwoSubjectAnnotation;
+
+public class BasicTwoSubjectAnnotation implements TwoSubjectAnnotation {
+	private GenomicAnnotation a;
+	private GenomicAnnotation b;
+	
+	public BasicTwoSubjectAnnotation() {
+		a = new BasicGenomicAnnotation("A");
+		b = new BasicGenomicAnnotation("B");
+	}
+	public GenomicAnnotation getA() {
+		return a;
+	}
+
+	public int getAEnd() {
+		return a.getEnd();
+	}
+
+	public String getAName() {
+		return a.getName();
+	}
+
+	public int getAStart() {
+		return a.getStart();
+	}
+
+	public GenomicAnnotation getB() {
+		return b;
+	}
+
+	public int getBEnd() {
+		return b.getEnd();
+	}
+
+	public String getBName() {
+		return b.getName();
+	}
+
+	public int getBStart() {
+		return b.getStart();
+	}
+	
+	public void setA(GenomicAnnotation a) { this.a = a;}
+	public void setB(GenomicAnnotation b) { this.b = b;}
+
+	public boolean isDirect() {
+		return !a.inReversedOrientation() && ! b.inReversedOrientation();
+	}
+	
+	public String toString() {
+		return getA().getChromosome() + ":" + getA().getStart() + "-" + getA().getEnd() + "\t("+(isDirect() ? "+" : "-")+")\t" + getB().getChromosome() + ":" + getB().getStart() + "-" + getB().getEnd();
+	}
+
+}
diff --git a/src/edu/mit/broad/prodinfo/datastrutures/Interval.java b/src/edu/mit/broad/prodinfo/datastrutures/Interval.java
new file mode 100644
index 0000000..ad54c11
--- /dev/null
+++ b/src/edu/mit/broad/prodinfo/datastrutures/Interval.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+package edu.mit.broad.prodinfo.datastrutures;
+
+/**
+ * Semi-open interval on the integer number line.
+ * Turf covered runs from the start value inclusive, up to, but not including, the end value.
+ *
+ * @author tsharpe
+ * @version $Revision: 51146 $
+ */
+public interface Interval
+{
+    // bit-wise definitions from which the other constants are composed
+    static final int HAS_LESSER_PART = 1;
+    static final int HAS_OVERLAPPING_PART = 2;
+    static final int HAS_GREATER_PART = 4;
+
+    static final int IS_ADJACENT_AND_EMPTY = 0;
+    static final int IS_STRICTLY_LESS = HAS_LESSER_PART; // 1
+    static final int IS_SUBSET = HAS_OVERLAPPING_PART; // 2
+    static final int IS_LEFT_OVERHANGING_OVERLAPPER = HAS_LESSER_PART | HAS_OVERLAPPING_PART; // 3
+    static final int IS_STRICTLY_GREATER = HAS_GREATER_PART; // 4
+    // there is no value that equals 5, since that would imply overhanging on left and right without overlapping
+    static final int IS_RIGHT_OVERHANGING_OVERLAPPER = HAS_GREATER_PART | HAS_OVERLAPPING_PART; // 6
+    static final int IS_SUPERSET = HAS_LESSER_PART | HAS_OVERLAPPING_PART | HAS_GREATER_PART; // 7
+
+    /**
+     * Returns the starting point of the interval.
+     * @return The start.
+     */
+    int getStart();
+
+    /**
+     * Returns the ending point of the interval.
+     * The interval is not regarded as including this point.
+     * @return The end.
+     */
+    int getEnd();
+
+    /**
+     * End - start.
+     */
+    int getLength();
+
+    /**
+     * Returns a constant that describes the relationship of this interval
+     * to a specified interval with regard to position on the number line.
+     * @param interval The interval to compare this one to.
+     * @return One of the IS_* constants defined above.
+     */
+    int getRelationship( Interval interval );
+
+    /**
+     * Returns true if this interval ends where the specified interval starts,
+     * or vice versa.
+     * @param interval The interval to compare this one to.
+     * @return True, if adjacent.
+     */
+    boolean isAdjacent( Interval interval );
+}
+
diff --git a/src/edu/mit/broad/prodinfo/datastrutures/IntervalTree.java b/src/edu/mit/broad/prodinfo/datastrutures/IntervalTree.java
new file mode 100644
index 0000000..fa751be
--- /dev/null
+++ b/src/edu/mit/broad/prodinfo/datastrutures/IntervalTree.java
@@ -0,0 +1,1267 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+package edu.mit.broad.prodinfo.datastrutures;
+
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * A Red-Black tree with intervals for keys.
+ * Not thread-safe, and cannot be made so.
+ *
+ * @author tsharpe
+ * @version $Revision: 51146 $
+ */
+public class IntervalTree<V>
+{
+	/**
+	 * Return the number of intervals in the tree.
+	 * @return The number of intervals.
+	 */
+	public int size()
+	{
+		return mRoot == null ? 0 : mRoot.getSize();
+	}
+	
+	public boolean isEmpty () 
+	{
+		return size() == 0;
+	}
+
+	/**
+	 * Remove all entries.
+	 */
+	public void clear()
+	{
+		mRoot = null;
+	}
+
+	/**
+	 * Put a new interval into the tree (or update the value associated with an existing interval).
+	 * If the interval is novel, the special sentinel value is returned.
+	 * @param interval The interval.
+	 * @param value The associated value.
+	 * @return The old value associated with that interval, or the sentinel.
+	 */
+	public V put( Interval interval, V value )
+	{
+		return put(interval.getStart(),interval.getEnd(),value);
+	}
+
+	/**
+	 * Put a new interval into the tree (or update the value associated with an existing interval).
+	 * If the interval is novel, the special sentinel value is returned.
+	 * @param start The interval's start.
+	 * @param end The interval's end.
+	 * @param value The associated value.
+	 * @return The old value associated with that interval, or the sentinel.
+	 */
+	@SuppressWarnings("null")
+	public V put( int start, int end, V value )
+	{
+		if ( start > end )
+			throw new IllegalArgumentException("Start cannot exceed end.");
+
+		V result = mSentinel;
+
+		if ( mRoot == null )
+		{
+			mRoot = new Node<V>(start,end,value);
+		}
+		else
+		{
+			Node<V> parent = null;
+			Node<V> node = mRoot;
+			int cmpVal = 0;
+
+			while ( node != null )
+			{
+				parent = node; // last non-null node
+				cmpVal = node.compare(start,end);
+				if ( cmpVal == 0 )
+				{
+					break;
+				}
+
+				node = cmpVal < 0 ? node.getLeft() : node.getRight();
+			}
+
+			if ( cmpVal == 0 )
+			{
+				result = parent.setValue(value);
+			}
+			else
+			{
+				if ( cmpVal < 0 )
+				{
+					mRoot = parent.insertLeft(start,end,value,mRoot);
+				}
+				else
+				{
+					mRoot = parent.insertRight(start,end,value,mRoot);
+				}
+			}
+		}
+
+		return result;
+	}
+
+	/**
+	 * Remove an interval from the tree.  If the interval does not exist in the tree the
+	 * special sentinel value is returned.
+	 * @param interval The interval to remove.
+	 * @return The value associated with that interval, or the sentinel.
+	 */
+	public V remove( Interval interval )
+	{
+		return remove(interval.getStart(),interval.getEnd());
+	}
+
+	/**
+	 * Remove an interval from the tree.  If the interval does not exist in the tree the
+	 * special sentinel value is returned.
+	 * @param start The interval's start.
+	 * @param end The interval's end.
+	 * @return The value associated with that interval, or the sentinel.
+	 */
+	public V remove( int start, int end )
+	{
+		V result = mSentinel;
+		Node<V> node = mRoot;
+
+		while ( node != null )
+		{
+			int cmpVal = node.compare(start,end);
+			if ( cmpVal == 0 )
+			{
+				result = node.getValue();
+				mRoot = node.remove(mRoot);
+				break;
+			}
+
+			node = cmpVal < 0 ? node.getLeft() : node.getRight();
+		}
+
+		return result;
+	}
+
+	/**
+	 * Find an interval.
+	 * @param interval The interval sought.
+	 * @return The Node that represents that interval, or null.
+	 */
+	public Node<V> find( Interval interval )
+	{
+		return find(interval.getStart(),interval.getEnd());
+	}
+
+	/**
+	 * Find an interval.
+	 * @param start The interval's start.
+	 * @param end The interval's end.
+	 * @return The Node that represents that interval, or null.
+	 */
+	public Node<V> find( int start, int end )
+	{
+		Node<V> node = mRoot;
+
+		while ( node != null )
+		{
+			int cmpVal = node.compare(start,end);
+			if ( cmpVal == 0 )
+			{
+				break;
+			}
+
+			node = cmpVal < 0 ? node.getLeft() : node.getRight();
+		}
+
+		return node;
+	}
+
+	/**
+	 * Find the nth interval in the tree.
+	 * @param idx The rank of the interval sought (from 0 to size()-1).
+	 * @return The Node that represents the nth interval.
+	 */
+	public Node<V> findByIndex( int idx )
+	{
+		return Node.findByRank(mRoot,idx+1);
+	}
+
+	/**
+	 * Find the rank of the specified interval.  If the specified interval is not in the
+	 * tree, then -1 is returned.
+	 * @param interval The interval for which the index is sought.
+	 * @return The rank of that interval, or -1.
+	 */
+	public int getIndex( Interval interval )
+	{
+		return getIndex(interval.getStart(),interval.getEnd());
+	}
+
+	/**
+	 * Find the rank of the specified interval.  If the specified interval is not in the
+	 * tree, then -1 is returned.
+	 * @param start The interval's start.
+	 * @param end The interval's end.
+	 * @return The rank of that interval, or -1.
+	 */
+	public int getIndex( int start, int end )
+	{
+		return Node.getRank(mRoot,start,end) - 1;
+	}
+
+	/**
+	 * Find the least interval in the tree.
+	 * @return The earliest interval, or null if the tree is empty.
+	 */
+	public Node<V> min()
+	{
+		Node<V> result = null;
+		Node<V> node = mRoot;
+
+		while ( node != null )
+		{
+			result = node;
+			node = node.getLeft();
+		}
+
+		return result;
+	}
+
+	/**
+	 * Find the earliest interval in the tree greater than or equal to the specified interval.
+	 * @param interval The interval sought. 
+	 * @return The earliest >= interval, or null if there is none.
+	 */
+	public Node<V> min( Interval interval )
+	{
+		return min(interval.getStart(),interval.getEnd());
+	}
+
+	/**
+	 * Find the earliest interval in the tree greater than or equal to the specified interval.
+	 * @param start The interval's start.
+	 * @param end The interval's end.
+	 * @return The earliest >= interval, or null if there is none.
+	 */
+	@SuppressWarnings("null")
+	public Node<V> min( int start, int end )
+	{
+		Node<V> result = null;
+		Node<V> node = mRoot;
+		int cmpVal = 0;
+
+		while ( node != null )
+		{
+			result = node;
+			cmpVal = node.compare(start,end);
+			if ( cmpVal == 0 )
+			{
+				break;
+			}
+
+			node = cmpVal < 0 ? node.getLeft() : node.getRight();
+		}
+
+		if ( cmpVal > 0 )
+		{
+			result = result.getNext();
+		}
+
+		return result;
+	}
+
+	/**
+	 * Find the earliest interval in the tree that overlaps the specified interval.
+	 * @param interval The interval sought. 
+	 * @return The earliest overlapping interval, or null if there is none.
+	 */
+	public Node<V> minOverlapper( Interval interval )
+	{
+		return minOverlapper(interval.getStart(),interval.getEnd());
+	}
+
+	/**
+	 * Find the earliest interval in the tree that overlaps the specified interval.
+	 * @param start The interval's start.
+	 * @param end The interval's end.
+	 * @return The earliest overlapping interval, or null if there is none.
+	 */
+	public Node<V> minOverlapper( int start, int end )
+	{
+		Node<V> result = null;
+		Node<V> node = mRoot;
+
+		if ( node != null && node.getMaxEnd() > start )
+		{
+			while ( true )
+			{
+				if ( node.getStart() < end && start < node.getEnd() )
+				{ // this node overlaps.  there might be a lesser overlapper down the left sub-tree.
+					// no need to consider the right sub-tree:  even if there's an overlapper, if won't be minimal
+					result = node;
+					node = node.getLeft();
+					if ( node == null || node.getMaxEnd() <= start )
+						break; // no left sub-tree or all nodes end too early
+				}
+				else
+				{ // no overlap.  if there might be a left sub-tree overlapper, consider the left sub-tree.
+					Node<V> left = node.getLeft();
+					if ( left != null && left.getMaxEnd() > start )
+					{
+						node = left;
+					}
+					else
+					{ // left sub-tree cannot contain an overlapper.  consider the right sub-tree.
+						if ( node.getStart() >= end )
+							break; // everything in the right sub-tree is past the end of the query interval
+
+						node = node.getRight();
+						if ( node == null || node.getMaxEnd() <= start )
+							break; // no right sub-tree or all nodes end too early
+					}
+				}
+			}
+		}
+
+		return result;
+	}
+
+	/**
+	 * Find the greatest interval in the tree.
+	 * @return The latest interval, or null if the tree is empty.
+	 */
+	public Node<V> max()
+	{
+		Node<V> result = null;
+		Node<V> node = mRoot;
+
+		while ( node != null )
+		{
+			result = node;
+			node = node.getRight();
+		}
+
+		return result;
+	}
+
+	/**
+	 * Find the latest interval in the tree less than or equal to the specified interval.
+	 * @param interval The interval sought. 
+	 * @return The latest <= interval, or null if there is none.
+	 */
+	public Node<V> max( Interval interval )
+	{
+		return max(interval.getStart(),interval.getEnd());
+	}
+
+	/**
+	 * Find the latest interval in the tree less than or equal to the specified interval.
+	 * @param start The interval's start.
+	 * @param end The interval's end.
+	 * @return The latest >= interval, or null if there is none.
+	 */
+	@SuppressWarnings("null")
+	public Node<V> max( int start, int end )
+	{
+		Node<V> result = null;
+		Node<V> node = mRoot;
+		int cmpVal = 0;
+
+		while ( node != null )
+		{
+			result = node;
+			cmpVal = node.compare(start,end);
+			if ( cmpVal == 0 )
+			{
+				break;
+			}
+
+			node = cmpVal < 0 ? node.getLeft() : node.getRight();
+		}
+
+		if ( cmpVal < 0 )
+		{
+			result = result.getPrev();
+		}
+
+		return result;
+	}
+
+	/**
+	 * Return an iterator over the entire tree.
+	 * @return An iterator.
+	 */
+	public Iterator<Node<V>> iterator()
+	{
+		return new FwdIterator(min());
+	}
+	
+	/**
+	 * Return an iterator over the entire tree values
+	 * @return An iterator
+	 */
+	public Iterator<V> valueIterator() 
+	{
+		return new ValuesIterator<V>(iterator());
+	}
+
+	/**
+	 * Return an iterator over all intervals greater than or equal to the specified interval.
+	 * @param interval The minimum interval.
+	 * @return An iterator.
+	 */
+	public Iterator<Node<V>> iterator( Interval interval )
+	{
+		return new FwdIterator(min(interval.getStart(),interval.getEnd()));
+	}
+
+	/**
+	 * Return an iterator over all intervals greater than or equal to the specified interval.
+	 * @param start The interval's start.
+	 * @param end The interval's end.
+	 * @return An iterator.
+	 */
+	public Iterator<Node<V>> iterator( int start, int end )
+	{
+		return new FwdIterator(min(start,end));
+	}
+
+	/**
+	 * Return an iterator over all intervals overlapping the specified range.
+	 * @param start The range start.
+	 * @param end The range end.
+	 * @return An iterator.
+	 */
+	public Iterator<Node<V>> overlappers( int start, int end )
+	{
+		return new OverlapIterator(start,end);
+	}
+
+	/**
+	 * Return an iterator over the entire tree that returns intervals in reverse order.
+	 * @return An iterator.
+	 */
+	public Iterator<Node<V>> reverseIterator()
+	{
+		return new RevIterator(max());
+	}
+
+	/**
+	 * Return an iterator over all intervals less than or equal to the specified interval, in reverse order.
+	 * @param interval The maximum interval.
+	 * @return An iterator.
+	 */
+	public Iterator<Node<V>> reverseIterator( Interval interval )
+	{
+		return new RevIterator(max(interval.getStart(),interval.getEnd()));
+	}
+
+	/**
+	 * Return an iterator over all intervals less than or equal to the specified interval, in reverse order.
+	 * @param start The interval's start.
+	 * @param end The interval's end.
+	 * @return An iterator.
+	 */
+	public Iterator<Node<V>> reverseIterator( int start, int end )
+	{
+		return new RevIterator(max(start,end));
+	}
+
+	/**
+	 * Get the special sentinel value that will be used to signal novelty when putting a new interval
+	 * into the tree, or to signal "not found" when removing an interval.  This is null by default.
+	 * @return The sentinel value.
+	 */
+	public V getSentinel()
+	{
+		return mSentinel;
+	}
+
+	/**
+	 * Set the special sentinel value that will be used to signal novelty when putting a new interval
+	 * into the tree, or to signal "not found" when removing an interval.
+	 * @param sentinel The new sentinel value.
+	 * @return The old sentinel value.
+	 */
+	public V setSentinel( V sentinel )
+	{
+		V result = mSentinel;
+		mSentinel = sentinel;
+		return result;
+	}
+
+	void removeNode( Node<V> node )
+	{
+		mRoot = node.remove(mRoot);
+	}
+
+	private Node<V> mRoot;
+	private V mSentinel;
+
+	public static class Node<V1>
+	implements Interval
+	{
+		Node( int start, int end, V1 value )
+		{
+			mStart = start;
+			mEnd = end;
+			mValue = value;
+			mSize = 1;
+			mMaxEnd = mEnd;
+			mIsBlack = true;
+		}
+
+		Node( Node<V1> parent, int start, int end, V1 value )
+		{
+			mParent = parent;
+			mStart = start;
+			mEnd = end;
+			mValue = value;
+			mMaxEnd = mEnd;
+			mSize = 1;
+		}
+
+		public int getStart()
+		{
+			return mStart;
+		}
+
+		public int getEnd()
+		{
+			return mEnd;
+		}
+
+		public int getLength()
+		{
+			return mEnd - mStart;
+		}
+
+		public int getRelationship( Interval interval )
+		{
+			int result = 0;
+			if ( mStart < interval.getStart() )
+				result = Interval.HAS_LESSER_PART;
+			if ( mEnd > interval.getEnd() )
+				result |= Interval.HAS_GREATER_PART;
+			if ( mStart < interval.getEnd() && interval.getStart() < mEnd )
+				result |= Interval.HAS_OVERLAPPING_PART;
+			return result;
+		}
+
+		public boolean isAdjacent( Interval interval )
+		{
+			return mStart == interval.getEnd() || mEnd == interval.getStart();
+		}
+
+		public V1 getValue()
+		{
+			return mValue;
+		}
+
+		public V1 setValue( V1 value )
+		{
+			V1 result = mValue;
+			mValue = value;
+			return result;
+		}
+
+		int getSize()
+		{
+			return mSize;
+		}
+
+		int getMaxEnd()
+		{
+			return mMaxEnd;
+		}
+
+		Node<V1> getLeft()
+		{
+			return mLeft;
+		}
+
+		Node<V1> insertLeft( int start, int end, V1 value, Node<V1> root )
+		{
+			mLeft = new Node<V1>(this,start,end,value);
+			return insertFixup(mLeft,root);
+		}
+
+		Node<V1> getRight()
+		{
+			return mRight;
+		}
+
+		Node<V1> insertRight( int start, int end, V1 value, Node<V1> root )
+		{
+			mRight = new Node<V1>(this,start,end,value);
+			return insertFixup(mRight,root);
+		}
+
+		public Node<V1> getNext()
+		{
+			Node<V1> result;
+
+			if ( mRight != null )
+			{
+				result = mRight;
+				while ( result.mLeft != null )
+				{
+					result = result.mLeft;
+				}
+			}
+			else
+			{
+				Node<V1> node = this;
+				result = mParent;
+				while ( result != null && node == result.mRight )
+				{
+					node = result;
+					result = result.mParent;
+				}
+			}
+
+			return result;
+		}
+
+		public Node<V1> getPrev()
+		{
+			Node<V1> result;
+
+			if ( mLeft != null )
+			{
+				result = mLeft;
+				while ( result.mRight != null )
+				{
+					result = result.mRight;
+				}
+			}
+			else
+			{
+				Node<V1> node = this;
+				result = mParent;
+				while ( result != null && node == result.mLeft )
+				{
+					node = result;
+					result = result.mParent;
+				}
+			}
+
+			return result;
+		}
+
+		boolean wasRemoved()
+		{
+			return mSize == 0;
+		}
+
+		Node<V1> remove( Node<V1> root )
+		{
+			if ( mSize == 0 )
+			{
+				throw new IllegalStateException("Entry was already removed.");
+			}
+
+			if ( mLeft == null )
+			{
+				if ( mRight == null )
+				{ // no children
+					if ( mParent == null )
+					{
+						root = null;
+					}
+					else if ( mParent.mLeft == this )
+					{
+						mParent.mLeft = null;
+						fixup(mParent);
+
+						if ( mIsBlack )
+							root = removeFixup(mParent,null,root);
+					}
+					else
+					{
+						mParent.mRight = null;
+						fixup(mParent);
+
+						if ( mIsBlack )
+							root = removeFixup(mParent,null,root);
+					}
+				}
+				else
+				{ // single child on right
+					root = spliceOut(mRight,root);
+				}
+			}
+			else if ( mRight == null )
+			{ // single child on left
+				root = spliceOut(mLeft,root);
+			}
+			else
+			{ // two children
+				Node<V1> next = getNext();
+			root = next.remove(root);
+
+			// put next into tree in same position as this, effectively removing this
+			if ( (next.mParent = mParent) == null )
+				root = next;
+			else if ( mParent.mLeft == this )
+				mParent.mLeft = next;
+			else
+				mParent.mRight = next;
+
+			if ( (next.mLeft = mLeft) != null )
+			{
+				mLeft.mParent = next;
+			}
+
+			if ( (next.mRight = mRight) != null )
+			{
+				mRight.mParent = next;
+			}
+
+			next.mIsBlack = mIsBlack;
+			next.mSize = mSize;
+			}
+
+			mSize = 0;
+			return root;
+		}
+
+		// backwards comparison!  compares start+end to this.
+		int compare( int start, int end )
+		{
+			int result = 0;
+
+			if ( start > mStart )
+				result = 1;
+			else if ( start < mStart )
+				result = -1;
+			else if ( end > mEnd )
+				result = 1;
+			else if ( end < mEnd )
+				result = -1;
+
+			return result;
+		}
+
+		@SuppressWarnings("null")
+		static <V1> Node<V1> getNextOverlapper( Node<V1> node, int start, int end )
+		{
+			do
+			{
+				Node<V1> nextNode = node.mRight;
+				if ( nextNode != null && nextNode.mMaxEnd > start )
+				{
+					node = nextNode;
+					while ( (nextNode = node.mLeft) != null && nextNode.mMaxEnd > start )
+						node = nextNode;
+				}
+				else
+				{
+					nextNode = node;
+					while ( (node = nextNode.mParent) != null && node.mRight == nextNode )
+						nextNode = node;
+				}
+
+				if ( node != null && node.mStart >= end )
+					node = null;
+			}
+			while ( node != null && !(node.mStart < end && start < node.mEnd) );
+
+			return node;
+		}
+
+		static <V1> Node<V1> findByRank( Node<V1> node, int rank )
+		{
+			while ( node != null )
+			{
+				int nodeRank = node.getRank();
+				if ( rank == nodeRank )
+					break;
+
+				if ( rank < nodeRank )
+				{
+					node = node.mLeft;
+				}
+				else
+				{
+					node = node.mRight;
+					rank -= nodeRank;
+				}
+			}
+
+			return node;
+		}
+
+		static <V1> int getRank( Node<V1> node, int start, int end )
+		{
+			int rank = 0;
+
+			while ( node != null )
+			{
+				int cmpVal = node.compare(start,end);
+				if ( cmpVal < 0 )
+				{
+					node = node.mLeft;
+				}
+				else
+				{
+					rank += node.getRank();
+					if ( cmpVal == 0 )
+						return rank; // EARLY RETURN!!!
+
+						node = node.mRight;
+				}
+			}
+
+			return 0;
+		}
+
+		private int getRank()
+		{
+			int result = 1;
+			if ( mLeft != null )
+				result = mLeft.mSize + 1;
+			return result;
+		}
+
+		private Node<V1> spliceOut( Node<V1> child, Node<V1> root )
+		{
+			if ( (child.mParent = mParent) == null )
+			{
+				root = child;
+				child.mIsBlack = true;
+			}
+			else
+			{
+				if ( mParent.mLeft == this )
+					mParent.mLeft = child;
+				else
+					mParent.mRight = child;
+				fixup(mParent);
+
+				if ( mIsBlack )
+					root = removeFixup(mParent,child,root);
+			}
+
+			return root;
+		}
+
+		private Node<V1> rotateLeft( Node<V1> root )
+		{
+			Node<V1> child = mRight;
+
+			int childSize = child.mSize;
+			child.mSize = mSize;
+			mSize -= childSize;
+
+			if ( (mRight = child.mLeft) != null )
+			{
+				mRight.mParent = this;
+				mSize += mRight.mSize;
+			}
+
+			if ( (child.mParent = mParent) == null )
+				root = child;
+			else if ( this == mParent.mLeft )
+				mParent.mLeft = child;
+			else
+				mParent.mRight = child;
+
+			child.mLeft = this;
+			mParent = child;
+
+			setMaxEnd();
+			child.setMaxEnd();
+
+			return root;
+		}
+
+		private Node<V1> rotateRight( Node<V1> root )
+		{
+			Node<V1> child = mLeft;
+
+			int childSize = child.mSize;
+			child.mSize = mSize;
+			mSize -= childSize;
+
+			if ( (mLeft = child.mRight) != null )
+			{
+				mLeft.mParent = this;
+				mSize += mLeft.mSize;
+			}
+
+			if ( (child.mParent = mParent) == null )
+				root = child;
+			else if ( this == mParent.mLeft )
+				mParent.mLeft = child;
+			else
+				mParent.mRight = child;
+
+			child.mRight = this;
+			mParent = child;
+
+			setMaxEnd();
+			child.setMaxEnd();
+
+			return root;
+		}
+
+		private void setMaxEnd()
+		{
+			mMaxEnd = mEnd;
+			if ( mLeft != null )
+				mMaxEnd = Math.max(mMaxEnd,mLeft.mMaxEnd);
+			if ( mRight != null )
+				mMaxEnd = Math.max(mMaxEnd,mRight.mMaxEnd);
+		}
+
+		private static <V1> void fixup( Node<V1> node )
+		{
+			do
+			{
+				node.mSize = 1;
+				node.mMaxEnd = node.mEnd;
+				if ( node.mLeft != null )
+				{
+					node.mSize += node.mLeft.mSize;
+					node.mMaxEnd = node.mMaxEnd > node.mLeft.mMaxEnd  ? node.mMaxEnd : node.mLeft.mMaxEnd;
+                                        //Math.max(node.mMaxEnd,node.mLeft.mMaxEnd);
+				}
+				if ( node.mRight != null )
+				{
+					node.mSize += node.mRight.mSize;
+                                        node.mMaxEnd = node.mMaxEnd > node.mRight.mMaxEnd ? node.mMaxEnd : node.mRight.mMaxEnd;
+					//node.mMaxEnd = Math.max(node.mMaxEnd,node.mRight.mMaxEnd);
+				}
+			}
+			while ( (node = node.mParent) != null );
+		}
+
+		private static <V1> Node<V1> insertFixup( Node<V1> daughter, Node<V1> root )
+		{
+			Node<V1> mom = daughter.mParent;
+			fixup(mom);
+
+			while( mom != null && !mom.mIsBlack )
+			{
+				Node<V1> gramma = mom.mParent;
+				Node<V1> auntie = gramma.mLeft;
+				if ( auntie == mom )
+				{
+					auntie = gramma.mRight;
+					if ( auntie != null && !auntie.mIsBlack )
+					{
+						mom.mIsBlack = true;
+						auntie.mIsBlack = true;
+						gramma.mIsBlack = false;
+						daughter = gramma;
+					}
+					else
+					{
+						if ( daughter == mom.mRight )
+						{
+							root = mom.rotateLeft(root);
+							mom = daughter;
+						}
+						mom.mIsBlack = true;
+						gramma.mIsBlack = false;
+						root = gramma.rotateRight(root);
+						break;
+					}
+				}
+				else
+				{
+					if ( auntie != null && !auntie.mIsBlack )
+					{
+						mom.mIsBlack = true;
+						auntie.mIsBlack = true;
+						gramma.mIsBlack = false;
+						daughter = gramma;
+					}
+					else
+					{
+						if ( daughter == mom.mLeft )
+						{
+							root = mom.rotateRight(root);
+							mom = daughter;
+						}
+						mom.mIsBlack = true;
+						gramma.mIsBlack = false;
+						root = gramma.rotateLeft(root);
+						break;
+					}
+				}
+				mom = daughter.mParent;
+			}
+			root.mIsBlack = true;
+			return root;
+		}
+
+		private static <V1> Node<V1> removeFixup( Node<V1> parent, Node<V1> node, Node<V1> root )
+		{
+			do
+			{
+				if ( node == parent.mLeft )
+				{
+					Node<V1> sister = parent.mRight;
+					if ( !sister.mIsBlack )
+					{
+						sister.mIsBlack = true;
+						parent.mIsBlack = false;
+						root = parent.rotateLeft(root);
+						sister = parent.mRight;
+					}
+					if ( (sister.mLeft == null || sister.mLeft.mIsBlack) && (sister.mRight == null || sister.mRight.mIsBlack) )
+					{
+						sister.mIsBlack = false;
+						node = parent;
+					}
+					else
+					{
+						if ( sister.mRight == null || sister.mRight.mIsBlack )
+						{
+							sister.mLeft.mIsBlack = true;
+							sister.mIsBlack = false;
+							root = sister.rotateRight(root);
+							sister = parent.mRight;
+						}
+						sister.mIsBlack = parent.mIsBlack;
+						parent.mIsBlack = true;
+						sister.mRight.mIsBlack = true;
+						root = parent.rotateLeft(root);
+						node = root;
+					}
+				}
+				else
+				{
+					Node<V1> sister = parent.mLeft;
+					if ( !sister.mIsBlack )
+					{
+						sister.mIsBlack = true;
+						parent.mIsBlack = false;
+						root = parent.rotateRight(root);
+						sister = parent.mLeft;
+					}
+					if ( (sister.mLeft == null || sister.mLeft.mIsBlack) && (sister.mRight == null || sister.mRight.mIsBlack) )
+					{
+						sister.mIsBlack = false;
+						node = parent;
+					}
+					else
+					{
+						if ( sister.mLeft == null || sister.mLeft.mIsBlack )
+						{
+							sister.mRight.mIsBlack = true;
+							sister.mIsBlack = false;
+							root = sister.rotateLeft(root);
+							sister = parent.mLeft;
+						}
+						sister.mIsBlack = parent.mIsBlack;
+						parent.mIsBlack = true;
+						sister.mLeft.mIsBlack = true;
+						root = parent.rotateRight(root);
+						node = root;
+					}
+				}
+				parent = node.mParent;
+			}
+			while ( parent != null && node.mIsBlack );
+
+			node.mIsBlack = true;
+			return root;
+		}
+
+		private Node<V1> mParent;
+		private Node<V1> mLeft;
+		private Node<V1> mRight;
+		private int mStart;
+		private int mEnd;
+		private V1 mValue;
+		private int mSize;
+		private int mMaxEnd;
+		private boolean mIsBlack;
+	}
+
+	public class FwdIterator
+	implements Iterator<Node<V>>
+	{
+		public FwdIterator( Node<V> node )
+		{
+			mNext = node;
+		}
+
+		public boolean hasNext()
+		{
+			return mNext != null;
+		}
+
+		public Node<V> next()
+		{
+			if ( mNext == null )
+			{
+				throw new NoSuchElementException("No next element.");
+			}
+
+			if ( mNext.wasRemoved() )
+			{
+				mNext = min(mNext.getStart(),mNext.getEnd());
+				if ( mNext == null )
+					throw new ConcurrentModificationException("Current element was removed, and there are no more elements.");
+			}
+			mLast = mNext;
+			mNext = mNext.getNext();
+			return mLast;
+		}
+
+		public void remove()
+		{
+			if ( mLast == null )
+			{
+				throw new IllegalStateException("No entry to remove.");
+			}
+
+			removeNode(mLast);
+			mLast = null;
+		}
+
+		private Node<V> mNext;
+		private Node<V> mLast;
+	}
+
+	public class RevIterator
+	implements Iterator<Node<V>>
+	{
+		public RevIterator( Node<V> node )
+		{
+			mNext = node;
+		}
+
+		public boolean hasNext()
+		{
+			return mNext != null;
+		}
+
+		public Node<V> next()
+		{
+			if ( mNext == null )
+				throw new NoSuchElementException("No next element.");
+			if ( mNext.wasRemoved() )
+			{
+				mNext = max(mNext.getStart(),mNext.getEnd());
+				if ( mNext == null )
+					throw new ConcurrentModificationException("Current element was removed, and there are no more elements.");
+			}
+			mLast = mNext;
+			mNext = mNext.getPrev();
+			return mLast;
+		}
+
+		public void remove()
+		{
+			if ( mLast == null )
+			{
+				throw new IllegalStateException("No entry to remove.");
+			}
+
+			removeNode(mLast);
+			mLast = null;
+		}
+
+		private Node<V> mNext;
+		private Node<V> mLast;
+	}
+
+	public class OverlapIterator
+	implements Iterator<Node<V>>
+	{
+		public OverlapIterator( int start, int end )
+		{
+			mNext = minOverlapper(start,end);
+			mStart = start;
+			mEnd = end;
+		}
+
+		public boolean hasNext()
+		{
+			return mNext != null;
+		}
+
+		public Node<V> next()
+		{
+			if ( mNext == null )
+			{
+				throw new NoSuchElementException("No next element.");
+			}
+
+			if ( mNext.wasRemoved() )
+			{
+				throw new ConcurrentModificationException("Current element was removed.");
+			}
+
+			mLast = mNext;
+			mNext = Node.getNextOverlapper(mNext,mStart,mEnd);
+			return mLast;
+		}
+
+		public void remove()
+		{
+			if ( mLast == null )
+			{
+				throw new IllegalStateException("No entry to remove.");
+			}
+
+			removeNode(mLast);
+			mLast = null;
+		}
+
+		private Node<V> mNext;
+		private Node<V> mLast;
+		private int mStart;
+		private int mEnd;
+	}
+
+	public static class ValuesIterator<V1>
+	implements Iterator<V1>
+	{
+		public ValuesIterator( Iterator<Node<V1>> itr )
+		{
+			mItr = itr;
+		}
+
+		public boolean hasNext()
+		{
+			return mItr.hasNext();
+		}
+
+		public V1 next()
+		{
+			return mItr.next().getValue();
+		}
+
+		public void remove()
+		{
+			mItr.remove();
+		}
+
+		private Iterator<Node<V1>> mItr;
+	}
+}
diff --git a/src/edu/mit/broad/prodinfo/genomicplot/Feature.java b/src/edu/mit/broad/prodinfo/genomicplot/Feature.java
new file mode 100644
index 0000000..fcdb450
--- /dev/null
+++ b/src/edu/mit/broad/prodinfo/genomicplot/Feature.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package edu.mit.broad.prodinfo.genomicplot;
+
+public interface Feature {
+	String getName();
+	double getScore();
+}
diff --git a/src/edu/mit/broad/prodinfo/genomicplot/GenomicAnnotation.java b/src/edu/mit/broad/prodinfo/genomicplot/GenomicAnnotation.java
new file mode 100644
index 0000000..c9c3dd4
--- /dev/null
+++ b/src/edu/mit/broad/prodinfo/genomicplot/GenomicAnnotation.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package edu.mit.broad.prodinfo.genomicplot;
+
+import java.util.List;
+
+import edu.mit.broad.prodinfo.sequence.Sequence;
+
+public interface GenomicAnnotation extends Cloneable, Comparable<GenomicAnnotation> ,Feature {
+	int getStart();
+	int getEnd();
+	void setScore(double score);
+	void setName(String name);
+	//String getDisplayName();
+	boolean inReversedOrientation();
+	void setReversedOrientation(boolean isInReversedOrientation);
+	String getOrientation();
+	long getMiddle();
+	
+	int getLength();
+	
+	void setStart(int start);
+	void setEnd(int end);
+	Sequence getSequence();
+	void setSequence(Sequence seq);
+	String getChromosome();
+	void setChromosome(String chr);
+	
+	public void setOrientation(String orientationString);
+	
+	/**
+	 * Calculates the distance to the another genomic annotation.
+	 * @return 0 if the annotations overlap or the minimum between the edges otherwise.
+	 */
+	public int getDistanceTo(GenomicAnnotation other);
+	
+	/**
+	 * Genomic annotation integer identification if such exists.
+	 */
+	public String getId();
+	public void setId(String id);
+	
+	/**
+	 * 
+	 * @param other - other genomic annotation
+	 * @param buffer if the overlap is within buffer they will be considered overlapping 
+	 *        even if they do not overlap within their original boundaries.
+	 * @return true if they overlap in this extended definition
+	 */
+	public boolean overlaps(GenomicAnnotation other, int buffer);
+	/**
+	 * 
+	 * @param other GenomicAnnotation to check of overlap
+	 * @return true if the current instance overlaps with the other one.
+	 */
+	public boolean overlaps(GenomicAnnotation other);
+	
+	/**
+	 * Change the current instance to represent its intersection
+	 * with the provided annotation 
+	 * @param other
+	 */
+	public void takeIntersection(GenomicAnnotation other);
+	
+	/**
+	 * Change the current instance to represent its union
+	 * with the provided annotation 
+	 * @param GenomicAnnotation other
+	 */
+	public void takeUnion(GenomicAnnotation other);
+	
+	/**
+	 * Enlarges this annotation by 
+	 * @param other
+	 */
+	public void stitchTo(GenomicAnnotation other);
+	
+	/**
+	 * Checks whether a twoSubjectAnnotation flanks the instance
+	 */
+	boolean isFlankedBy(TwoSubjectAnnotation twoSubjectAnnotation, int buffer);
+	
+	
+	/**
+	 * @return int - the five prime buffer (i.e. bases not belonging to the annotation proper).
+	 */
+	public int getFivePrimeBases();
+
+	/**
+	 * @return int - the three prime buffer (i.e. bases not belonging to the annotation proper).
+	 */
+	public int getThreePrimeBases();
+	
+	public boolean contains(GenomicAnnotation other);
+	
+	/**
+	 * Returns the difference (all regions in this genomic annotation not in the given list)
+	 * between this genomic annotation and the given list
+	 * 
+	 * @param others - the annotations to take the difference against
+	 * @return A List of genomic annotations of the resulting difference.
+	 */
+	public List<GenomicAnnotation> minus(List<? extends GenomicAnnotation> others); 
+	
+	/**
+	 * Returns the difference (all regions in this genomic annotation not in the given other annotation)
+	 * between this genomic annotation and the one
+	 * 
+	 * @param other - the annotations to take the difference against
+	 * @return A List of genomic annotations of the resulting difference.
+	 */
+	public List<GenomicAnnotation> minus(GenomicAnnotation other);
+	
+	/**
+	 * Fragments the current annotation if it overlaps the provided one.
+	 * it returns an empty list if this annotation does not ovelap the
+	 * one passed in.
+	 * 
+	 * @param GenomicAnnotation annotation to disect the current one
+	 * @return List<GenomicAnnotation> of the disected annotations, an empty list is returned if 
+	 * 			the annotations do not overlap.
+	 */
+	List<GenomicAnnotation> disect(GenomicAnnotation a);
+	
+	
+	/**
+	 * Fragments the current annotation if it overlaps the provided ones.
+	 * It returns a list with one component (this annotation) if no annotation 
+	 * in the provided list overlaps the discted annotaion.
+	 * 
+	 * @param List<GenomicAnnotation> <b>sorted</b> annotations with which to disect the current one.
+	 * @return List<GenomicAnnotation> of the disected annotations, a list just this annotation is returned if 
+	 * 			the annotations do not overlap.
+	 */
+	List<GenomicAnnotation> disect(List<? extends GenomicAnnotation> disectors);
+	
+	
+	/**
+	 * Gets the start of the annotation considering its orientation 
+	 * @return getStart() if the feature is in direct orientation or getEnd() otherwise
+	 */
+	int getOrientedStart();
+	
+	/**
+	 * Gets the end of the annotation considering its orientation 
+	 * @return getEnd() if the feature is in direct orientation or getStart() otherwise
+	 */
+	int getOrientedEnd();
+	
+	/**
+	 * Returns a string of the form chr<chromosome>:start-end
+	 * @return
+	 */
+	public String getLocationString();
+
+	/**
+	 * Returns the ussual way to write a chromosome: chrSymbol if a chromosome or the full name of the scaffold 
+	 * 
+	 */
+	public String getChromosomeString();
+	
+	/**
+	 * Returns true if the annotation (like a full BED or a RefSeq) has sub annotations like exons or blocks
+	 */
+	public boolean mayHaveBlocks();
+	
+	/**
+	 * Returns a list of blocks if the annotations has any (@see containsBlocks)
+	 */
+	public List<? extends GenomicAnnotation> getBlocks();
+	
+	/**
+	 * Adds a block to the annotation if it supports blocks
+	 */
+	public void addBlock(String name, int start, int end);
+}
diff --git a/src/edu/mit/broad/prodinfo/genomicplot/ParseException.java b/src/edu/mit/broad/prodinfo/genomicplot/ParseException.java
new file mode 100644
index 0000000..886b97a
--- /dev/null
+++ b/src/edu/mit/broad/prodinfo/genomicplot/ParseException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package edu.mit.broad.prodinfo.genomicplot;
+
+public class ParseException extends Exception {
+
+	public ParseException() {
+		super();
+		// TODO Auto-generated constructor stub
+	}
+
+	public ParseException(String arg0) {
+		super(arg0);
+		// TODO Auto-generated constructor stub
+	}
+
+	public ParseException(String arg0, Throwable arg1) {
+		super(arg0, arg1);
+		// TODO Auto-generated constructor stub
+	}
+
+	public ParseException(Throwable arg0) {
+		super(arg0);
+		// TODO Auto-generated constructor stub
+	}
+
+}
diff --git a/src/edu/mit/broad/prodinfo/genomicplot/TwoSubjectAnnotation.java b/src/edu/mit/broad/prodinfo/genomicplot/TwoSubjectAnnotation.java
new file mode 100644
index 0000000..e47b7c8
--- /dev/null
+++ b/src/edu/mit/broad/prodinfo/genomicplot/TwoSubjectAnnotation.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package edu.mit.broad.prodinfo.genomicplot;
+
+public interface TwoSubjectAnnotation {
+	int getAStart();
+	int getBStart();
+	int getAEnd();
+	int getBEnd();
+	String getAName();
+	String getBName();
+	
+	GenomicAnnotation getA();
+	GenomicAnnotation getB();
+	
+	boolean isDirect();
+
+}
diff --git a/src/edu/mit/broad/prodinfo/multiplealignment/MAFAlignment.java b/src/edu/mit/broad/prodinfo/multiplealignment/MAFAlignment.java
new file mode 100644
index 0000000..57251b3
--- /dev/null
+++ b/src/edu/mit/broad/prodinfo/multiplealignment/MAFAlignment.java
@@ -0,0 +1,853 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package edu.mit.broad.prodinfo.multiplealignment;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Stack;
+
+//import Jama.Matrix;
+import edu.mit.broad.prodinfo.chromosome.BasicGenomicAnnotation;
+import edu.mit.broad.prodinfo.datastrutures.IntervalTree;
+import edu.mit.broad.prodinfo.datastrutures.IntervalTree.Node;
+import edu.mit.broad.prodinfo.genomicplot.GenomicAnnotation;
+import edu.mit.broad.prodinfo.genomicplot.ParseException;
+import edu.mit.broad.prodinfo.sequence.SequenceRegion;
+
+public class MAFAlignment extends MultipleAlignment {
+	private IntervalTree<MAFMultipleAlignmentBlock> alignmentBlockTree;
+	private List<String> sequenceIds;
+
+	
+	MAFHeader header;
+	//Map<Integer, Long> index;
+	IntervalTree<Long> index;
+	private String referenceChromosome;
+	
+	public MAFAlignment() {
+		super();
+		header = new MAFHeader();
+		alignmentBlockTree = new IntervalTree<MAFMultipleAlignmentBlock>();
+		sequenceIds = new ArrayList<String>();
+	}
+	
+	public MAFAlignment(String indexFile) throws IOException, ParseException {
+		this();
+		loadIndex(indexFile);
+		//System.out.println("Loaded index file " + index.keySet().size());
+	}
+	
+	public MAFHeader getHeader() { return header;}
+	
+	// MultipleAlignment overloading.
+	public void encode() {
+		//Iterator<MAFMultipleAlignmentBlock> alnBlockIt = alignmentBlocks.iterator();
+		Iterator<Node<MAFMultipleAlignmentBlock>> alnBlockIt = alignmentBlockTree.iterator();
+		while(alnBlockIt.hasNext()) {
+			//MultipleAlignment block = alnBlockIt.next();
+			Node<MAFMultipleAlignmentBlock> blockNode = alnBlockIt.next();
+			blockNode.getValue().encode();
+		}
+		
+	}
+	
+	public void encodeAsMatrix() {
+		//Iterator<MAFMultipleAlignmentBlock> alnBlockIt = alignmentBlocks.iterator();
+		Iterator<Node<MAFMultipleAlignmentBlock>> alnBlockIt = alignmentBlockTree.iterator();
+		while(alnBlockIt.hasNext()) {
+			//MultipleAlignment block = alnBlockIt.next();
+			Node<MAFMultipleAlignmentBlock> blockNode = alnBlockIt.next();
+			blockNode.getValue().encodeAsMatrix();
+		}		
+	}
+	
+	public void reverse() {
+		//Iterator<MAFMultipleAlignmentBlock> alnBlockIt = alignmentBlocks.iterator();
+		Iterator<Node<MAFMultipleAlignmentBlock>> alnBlockIt = alignmentBlockTree.iterator();
+		while(alnBlockIt.hasNext()) {
+			//MultipleAlignment block = alnBlockIt.next();
+			Node<MAFMultipleAlignmentBlock> blockNode = alnBlockIt.next();
+			blockNode.getValue().reverse();
+		}
+		/*
+		Stack<MAFMultipleAlignmentBlock> reversedBlocks = new Stack<MAFMultipleAlignmentBlock>();
+		while(alignmentBlocks.size() > 0) {
+			MAFMultipleAlignmentBlock block = alignmentBlocks.pop();
+			block.reverse();
+			reversedBlocks.push(block);
+		}			
+		alignmentBlocks = reversedBlocks;
+		*/
+	}
+	
+	public int length() {
+		int length = 0;
+		//if(!alignmentBlocks.isEmpty()) {
+		if(alignmentBlockTree.size() > 0) {
+			MAFMultipleAlignmentBlock first = alignmentBlockTree.min().getValue();//alignmentBlocks.get(0);
+			MAFMultipleAlignmentBlock last  = alignmentBlockTree.max().getValue();//alignmentBlocks.get(alignmentBlocks.size() - 1);
+			
+			length = last.getReferenceEnd()  - first.getReferenceStart();
+		}
+		
+		return length;
+	}
+	
+	/**
+	 * Assumes that encode has been called and each aligned sequence is encoded
+	 */
+	public Map<String, Short> getColumn(int i) {
+		Map<String, Short> col = getGapColumn();
+		Iterator<Node<MAFMultipleAlignmentBlock>> overlappingNodeIt = alignmentBlockTree.overlappers(i, i+1);
+
+		if(overlappingNodeIt.hasNext()) {
+			MAFMultipleAlignmentBlock containingBlock = overlappingNodeIt.next().getValue();
+			Map<String, Short> closestCol = containingBlock.getColumn(i);
+			Iterator<String> closestAlignSeqIdIt = closestCol.keySet().iterator();
+			while(closestAlignSeqIdIt.hasNext()) {
+				String seqId = closestAlignSeqIdIt.next();
+				col.put(seqId, closestCol.get(seqId));
+			}
+		}
+
+		return col;
+	}
+	
+	public void addShortEncodedColumn(Map<String, Short> col) {
+		throw new RuntimeException("Not yet implemented, to edit an alignment use a base MultipleAlignment MAF are ReadOnly");
+	}
+	
+	public void addShortEncodedRegion(Map<String, short[]> region) {
+		throw new RuntimeException("Not yet implemented, to edit an alignment use a base MultipleAlignment MAF are ReadOnly");
+	}
+	
+	public void addSequence(AlignedSequence seq) {
+		throw new RuntimeException("Not yet implemented, to edit an alignment use a base MultipleAlignment MAF are ReadOnly");
+	}
+
+	public void addSequences(List<AlignedSequence> sequences) {
+		throw new RuntimeException("Not yet implemented, to edit an alignment use a base MultipleAlignment MAF are ReadOnly");		
+	}
+	
+	public boolean isEmpty() { return alignmentBlockTree.isEmpty();}
+	
+	public void write(BufferedWriter bw) throws IOException {
+		if(getIOHelper() != null) {
+			super.write(bw);
+		} else {
+			header.addVariableValuePair("ref", getReferenceId());
+			header.write(bw);
+			Iterator<Node<MAFMultipleAlignmentBlock>> blockIt = alignmentBlockTree.iterator();
+			while(blockIt.hasNext()) {
+				blockIt.next().getValue().write(bw);
+			}
+		}
+		
+	}
+	
+	public MAFAlignment getSubAlignment(int refStart, int refEnd, boolean reverse) {
+		MAFAlignment subAln = new MAFAlignment();
+		subAln.setReferenceId(getReferenceId());
+		subAln.sequenceIds = this.sequenceIds;
+		Iterator<MAFMultipleAlignmentBlock> overlapperIt = 
+			new IntervalTree.ValuesIterator<MAFMultipleAlignmentBlock>(alignmentBlockTree.overlappers(refStart, refEnd));
+		GenomicAnnotation target = new BasicGenomicAnnotation(getReference());
+		target.setStart(refStart);
+		target.setEnd(refEnd);
+		while(overlapperIt.hasNext()) {
+			MAFMultipleAlignmentBlock overlappingBlock = overlapperIt.next();
+			subAln.addBlock(overlappingBlock.trim(target));
+		}
+		return subAln;
+	}
+	
+	public AlignedSequence getAlignedSequence(String sequenceId, boolean fillInGapsBetweenBlocks) {
+		//System.out.println("Is alignmentBlockTree empty? " + alignmentBlockTree.isEmpty());
+		if(!sequenceIds.contains(sequenceId) || alignmentBlockTree.isEmpty() ) {
+			//System.out.println("seqids " + sequenceIds + " does not contain <" + sequenceId +"> is contained in sequenceIds " + sequenceIds.contains(sequenceId));
+			return null;
+		}
+		
+		AlignedSequence seq = new AlignedSequence(sequenceId);
+		seq.setId(sequenceId);
+		if(getReferenceId().equals(sequenceId)) {
+			//System.out.println("Dealing with ref seq");
+			seq.setStart(alignmentBlockTree.min().getValue().getReferenceStart());
+			seq.setChromosome(alignmentBlockTree.min().getValue().getAlignedSequence(sequenceId).getChromosome());
+			seq.setEnd(alignmentBlockTree.max().getValue().getReferenceEnd());
+		}
+		Iterator<MAFMultipleAlignmentBlock> it = new IntervalTree.ValuesIterator<MAFMultipleAlignmentBlock>(alignmentBlockTree.iterator());
+		MAFMultipleAlignmentBlock lastBlock = null;
+		while(it.hasNext()) {
+			MAFMultipleAlignmentBlock block = it.next();
+			AlignedSequence segment = block.getAlignedSequence(sequenceId);
+			if(lastBlock!= null && lastBlock.getReferenceEnd() < block.getReferenceStart() && fillInGapsBetweenBlocks) {
+				//System.out.println("last block " + (lastBlock != null ? lastBlock.getReferenceStart()+"-"+lastBlock.getReferenceEnd():" not yet ") + " new block " + block.getReferenceStart()+"-"+block.getReferenceEnd() + ", appending " + (block.getReferenceStart() - lastBlock.getReferenceEnd()) + " Ns");
+				for(int i = 0; i < block.getReferenceStart() - lastBlock.getReferenceEnd(); i++) {
+					//System.out.println("\t\tappending gaps N");
+					seq.appendToSequence('N');
+				}
+			}
+			seq.appendToSequence(segment.getSequenceBases());
+			/*	
+			if(segment != null) {
+				seq.appendToSequence(segment.getSequenceBases());
+			} else {
+				for(int i = 0; i < blockNode.getValue().length(); i++) {
+					seq.appendToSequence('-');
+				}
+			}
+			*/
+			lastBlock = block;
+		}
+		return seq;
+	}
+	
+	public AlignedSequence getAlignedSequence(String sequenceId) { 
+		return getAlignedSequence(sequenceId, true);
+	}
+	
+	public List<AlignedSequence> getAlignedSequences(boolean fillGapsBetweenBlocks) {
+		ArrayList<AlignedSequence> sequences = new ArrayList<AlignedSequence>(sequenceIds.size());
+		Iterator<String> idIt = sequenceIds.iterator();
+		while(idIt.hasNext()) {
+			String id = idIt.next();
+			sequences.add(getAlignedSequence(id, fillGapsBetweenBlocks));
+		}
+		return sequences;		
+	}
+	public List<AlignedSequence> getAlignedSequences() {
+		return getAlignedSequences(true);
+	}
+	
+	public boolean overlaps(GenomicAnnotation annotation) {return index.overlappers(annotation.getStart(), annotation.getEnd()).hasNext();}
+	
+	public boolean contains(GenomicAnnotation annotation) {
+		return index.min().getStart() < annotation.getStart() && index.max().getEnd() > annotation.getEnd();
+	}
+	
+	public List<String> getAlignedSequenceIds() { return sequenceIds;}
+	
+	public int getReferenceStart() {
+		Node<MAFMultipleAlignmentBlock> first = alignmentBlockTree.min();
+		return first == null ? -1 : first.getValue().getReferenceStart();
+	}
+	
+	public int getReferenceEnd() {
+		Node<MAFMultipleAlignmentBlock> last = alignmentBlockTree.max();
+		return last == null ? -1 : last.getValue().getReferenceEnd();
+	}
+	
+	public void compress() {
+		Iterator<Node<MAFMultipleAlignmentBlock>> nodeIt =  alignmentBlockTree.iterator();
+		while(nodeIt.hasNext()) {
+			nodeIt.next().getValue().compress();
+		}
+	}
+	
+	public MultipleAlignment toMultipleAlignment(boolean fillGapsBetweenBlocks) {
+		MultipleAlignment ma = new MultipleAlignment();
+		ma.setReferenceId(getReferenceId());
+		ma.setRefGapped(true);
+		ma.addSequences(getAlignedSequences(fillGapsBetweenBlocks));
+		ma.getReference().setStart(getReferenceStart());
+		ma.getReference().setEnd(getReferenceEnd());
+		//System.out.println("to multiplealn #aln seqs: " + ma.length() + " ref? " + ma.getReferenceId());
+		return ma;		
+	}
+	
+	public MultipleAlignment toMultipleAlignment() {
+		return toMultipleAlignment(true);
+	}
+	
+	public MultipleAlignment sampleColumns(int colNum, int numConsecutiveCols) {
+		Random r = new Random();
+		int treeSize = alignmentBlockTree.size();
+		MultipleAlignment sampledAlignment = new MultipleAlignment();
+		sampledAlignment.setReferenceId(getReferenceId());
+		for(int i = 0; i < colNum - numConsecutiveCols + 1; i = numConsecutiveCols + i) {
+			int blockId = r.nextInt(treeSize);
+			System.out.println("randomly selected block " + blockId + " out of " + treeSize + " blocks" );
+			MAFMultipleAlignmentBlock block = alignmentBlockTree.findByIndex(blockId).getValue();
+			MultipleAlignment sampledCols = block.sampleColumns(1,numConsecutiveCols);
+			Iterator<String> seqIdIt = getAlignedSequenceIds().iterator();
+			while(seqIdIt.hasNext()) {
+				String seqId = seqIdIt.next();
+				if(sampledCols.getAlignedSequence(seqId) == null) {
+					AlignedSequence missingSeq = new AlignedSequence(seqId);
+					for(int j = 0; j < numConsecutiveCols; j++) {
+						missingSeq.appendToSequence('-');
+					}
+					sampledCols.addSequence(missingSeq);
+				}
+			}
+			sampledAlignment.append(sampledCols); //changed without test 5/23/08
+		}
+
+		return sampledAlignment;
+	}
+	
+	public void load (String fileName) throws IOException, ParseException {
+		load (fileName, 0, SequenceRegion.INF);
+	}
+	
+	public void load(RandomAccessFile handle, List<String> sequencesToLoad) throws IOException, ParseException {
+		load(handle, 1, SequenceRegion.INF, sequencesToLoad);
+	}
+	
+	public void load(RandomAccessFile handle, int referenceStart, int referenceEnd) throws IOException, ParseException {
+		load(handle, referenceStart, referenceEnd, new ArrayList<String>());
+	}
+	
+	public void load(RandomAccessFile handle, int referenceStart, int referenceEnd, List<String> sequencesToLoad) throws IOException, ParseException {
+		long offset = getClosestOffset(referenceStart);
+		//System.err.println("Starting to read file from " + offset + " from refstart " + referenceStart + " to refend " + referenceEnd);
+		alignmentBlockTree = new IntervalTree<MAFMultipleAlignmentBlock>();
+		handle.seek(offset);
+		boolean okToAdd = true;
+		BasicGenomicAnnotation reference = new BasicGenomicAnnotation("reference");
+		reference.setStart(referenceStart);
+		reference.setEnd(referenceEnd);
+		sequenceIds = new ArrayList<String>();
+		
+		if(sequencesToLoad != null) {
+			Iterator<String> seqIt = sequencesToLoad.iterator();
+			while(seqIt.hasNext()) {
+				sequenceIds.add(seqIt.next());
+			}
+		}
+		
+		String line = null;
+		String [] lineInfo = null;
+		Stack<MAFMultipleAlignmentBlock> alignmentBlockStack = new Stack<MAFMultipleAlignmentBlock>();
+		while((line = handle.readLine()) != null) {
+			//Ignore all comment lines
+			if(line.startsWith("#") || line.trim().length() == 0){
+				continue;
+			}
+			
+			//System.out.println(line);
+			if(line.startsWith("a ")) {
+				//System.err.println("New alignment: " + line);
+				//First check last alignment to see if it should be kept.
+				MAFMultipleAlignmentBlock lastMA = alignmentBlockStack.isEmpty()  ? null : alignmentBlockStack.pop();
+				if(lastMA != null) {
+					alignmentBlockTree.put(lastMA.getReferenceStart(), lastMA.getReferenceEnd(), lastMA.trim(reference));
+				}
+				/*
+				if(lastMA != null && referenceEnd > 0 && !lastMA.overlaps(reference)) {
+					alignments.pop(); // multiple alignment just built does overlap desired region.�
+					AlignedSequence refAln = lastMA.getAlignedSequence(reference.getContainingSequenceId());
+					System.out.println("Reference seq<"+reference.getContainingSequenceId() +"> lastMA aligned seqs : " + lastMA.getAlignedSequenceIds());
+					if (refAln.getStart() > referenceEnd) {
+						break;
+					}
+				}
+				*/
+				MAFMultipleAlignmentBlock ma = new MAFMultipleAlignmentBlock();
+				alignmentBlockStack.push(ma);
+				lineInfo = line.substring(2).split("\\s");
+				ma.setAlignmentInfoFromRawData(lineInfo);
+				okToAdd = true;
+			} else if(line.startsWith("s "))  {
+				if(alignmentBlockStack.isEmpty() || !okToAdd) {
+					continue;
+				}
+				//System.err.println("\tAlignment aligned seq " + line);
+				MAFMultipleAlignmentBlock ma = alignmentBlockStack.peek();
+				lineInfo = line.substring(2).split("\\s+");
+				AlignedSequence seq = ma.createSequenceFromRawData(lineInfo);
+				
+				if(getReferenceId() == null) {
+					setReferenceId(seq.getId());
+					setReferenceChromosome(seq.getChromosome());
+				} 
+				
+				if(ma.getReferenceId() == null) {
+					ma.setReferenceId(seq.getId());
+				}
+				
+				if(sequencesToLoad == null || sequencesToLoad.size() == 0 || sequencesToLoad.contains(seq.getId())) {
+					//System.err.println("YES: sequence " + seq.getId() + " chr " + seq.getChromosome() + " sequenceToLoad is null? " + (sequencesToLoad == null) + " is empty? " + (sequencesToLoad.size() == 0 ) + " or contains sequence " + sequencesToLoad.contains(seq.getId()));
+					ma.addSequence(seq);
+				} else {
+					//System.err.println("NO: sequence " + seq.getId() + " chr " + seq.getChromosome() + " sequenceToLoad is null? " + (sequencesToLoad == null) + " is empty? " + (sequencesToLoad.size() == 0 ) + " or contains sequence " + sequencesToLoad.contains(seq.getId()));
+					continue;
+				}
+				
+				//A bit wasteful, and should fix, but finally check if reference location is OK to add
+				if(getReferenceId().equals(seq.getId()) && seq.getEnd() <= referenceStart) {
+					alignmentBlockStack.pop();
+					okToAdd = false;
+				} else if(getReferenceId().equals(seq.getId()) && seq.getStart() >= referenceEnd) {
+					alignmentBlockStack.pop();
+					break;
+				}else {
+					//alignmentBlockStack.push(ma);
+					if(!getAlignedSequenceIds().contains(seq.getId())) {
+						sequenceIds.add(seq.getId());
+					}
+				}
+
+			} else if (line.startsWith("i ")) {
+				//We do not handle information lines yet.
+				continue;
+			}else if (line.startsWith("q ")) {
+				//We do not handle quality lines yet.
+				continue;
+			}else if (line.startsWith("e ") ) {
+				//We do not support e lines yet.
+				continue;
+			} else {
+				throw new ParseException("Invalid alignment line <"+ line +">");
+			}
+			
+		}
+		//Handle last alignment block
+		MAFMultipleAlignmentBlock lastMA = alignmentBlockStack.isEmpty()  ? null : alignmentBlockStack.pop();
+		if(lastMA != null && lastMA.getReferenceStart() < referenceEnd && lastMA.getReferenceEnd() > referenceStart) {
+			alignmentBlockTree.put(lastMA.getReferenceStart(), lastMA.getReferenceEnd(), lastMA.trim(reference));
+			//System.err.println("put last alignment: ");
+		}
+	}
+
+	public void load(String fileName, int referenceStart, int referenceEnd) 
+		throws IOException, ParseException {
+		RandomAccessFile raf = new RandomAccessFile(fileName, "r");
+		load(raf, referenceStart, referenceEnd);
+		raf.close();
+	}
+	
+	public IntervalTree<Long> getIndex() { return index;}
+	
+	public void writeIndex(String indexFileName) throws IOException {
+		Iterator<Node<Long>> idxEntryIt = index.iterator();
+		BufferedWriter bw = new BufferedWriter(new FileWriter(indexFileName));
+		while(idxEntryIt.hasNext()) {
+			Node<Long> entry = idxEntryIt.next();
+			bw.write(String.valueOf(entry.getStart()));
+			bw.write("\t" + String.valueOf(entry.getEnd() - entry.getStart()));
+			bw.write("\t" + String.valueOf(entry.getValue()));
+			bw.newLine();
+		}
+		bw.close();
+	}
+	
+	public void createIndex(String alignmentFile) throws IOException {
+		RandomAccessFile raf = new RandomAccessFile(alignmentFile,"r");
+		index = new IntervalTree<Long>();//LinkedHashMap<Integer, Long>();
+		String line;
+		String [] lineInfo= null;
+		long lastOffset = 0;
+		try {
+			boolean readNext = false;
+			while((line = raf.readLine()) != null) {
+				//Ignore all comment lines
+				if(line.startsWith("#") || line.trim().length() == 0){
+					continue;
+				}
+				
+				if(line.startsWith("a ")) {
+					readNext = true;
+					lastOffset = raf.getFilePointer() - line.getBytes().length - 1;
+				} else if(line.startsWith("s ")) {
+					if(readNext) {
+						lineInfo = line.split("\\s+");
+						int start = Integer.parseInt(lineInfo[2]);
+						int end   = Integer.parseInt(lineInfo[3]) + start;
+						index.put(start, end, lastOffset);
+					}
+					readNext = false;
+				}else if (line.startsWith("i ")) {
+					//We do not handle information lines yet.
+					continue;
+				}else if (line.startsWith("q ")) {
+					//We do not handle quality lines yet.
+					continue;
+				}else {
+					readNext = false;
+				}
+				
+			}
+		} finally {
+			try {
+				raf.close();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}		
+		
+	}
+	
+	public void setBlocks(List <MAFMultipleAlignmentBlock> blocks) {
+		alignmentBlockTree = new IntervalTree<MAFMultipleAlignmentBlock>();
+		Iterator<MAFMultipleAlignmentBlock> blockIt = blocks.iterator();
+		while(blockIt.hasNext()) {
+			addBlock(blockIt.next());
+		}
+
+	}
+	
+	public void addBlock(MAFMultipleAlignmentBlock block) throws IllegalArgumentException{
+		Iterator<Node<MAFMultipleAlignmentBlock>> overlappers = 
+			alignmentBlockTree.overlappers(block.getReferenceStart(), block.getReferenceEnd());
+		if(overlappers != null && overlappers.hasNext() ) {
+			throw new  IllegalArgumentException("A block in the alignment to append already overlaps exisiting alignment block. Can't append");
+		}
+		Iterator<String> alnSeqIdIt = block.getAlignedSequenceIds().iterator();
+		while(alnSeqIdIt.hasNext()) {
+			String seqId = alnSeqIdIt.next();
+			if(!sequenceIds.contains(seqId)) {
+				sequenceIds.add(seqId);
+			}
+		}
+		
+		alignmentBlockTree.put(block.getReferenceStart(), block.getReferenceEnd(), block);
+	}
+	
+	public void addBlocks(List<MAFMultipleAlignmentBlock> blocks) throws IllegalArgumentException {
+		addBlocks(blocks.iterator());
+	}
+	
+	public void addBlocks(Iterator<MAFMultipleAlignmentBlock> blockIt) throws IllegalArgumentException{
+		while(blockIt.hasNext()) {
+			addBlock(blockIt.next());
+		}
+	}	
+	
+	public void append(MAFAlignment mafAln) throws IllegalArgumentException{
+		addBlocks(mafAln.getBlockIterator());
+	}
+	
+	public void clear () {
+		alignmentBlockTree = new IntervalTree<MAFMultipleAlignmentBlock>();
+	}
+
+	long getClosestOffset(int position) {
+		Node<Long> node = index.max(position, position + 1);
+		return node == null ? 0 : node.getValue();
+	}
+	
+	public Iterator<MAFMultipleAlignmentBlock> getBlockIterator() {
+		final Iterator<Node<MAFMultipleAlignmentBlock>> nodeIt = alignmentBlockTree.iterator();
+		return new Iterator<MAFMultipleAlignmentBlock>() {
+
+			public boolean hasNext() {
+				return nodeIt.hasNext();
+			}
+
+			public MAFMultipleAlignmentBlock next() {
+				return nodeIt.next().getValue();
+			}
+
+			public void remove() {
+				nodeIt.remove();
+			}
+			
+		};
+		
+
+	}
+	
+	private void setReferenceChromosome(String chromosome) {
+		this.referenceChromosome = chromosome;
+		
+	}
+	
+	private String getReferenceChromosome() {return this.referenceChromosome;	}
+
+	private void padN(int start, int end) {
+		if(getReference() == null) {
+			return;
+		}
+		
+		int startPadLength = getReferenceStart() - start;
+		int endPadLength   = end - getReferenceEnd();
+		if(startPadLength > 0) {
+			MAFMultipleAlignmentBlock pad = new MAFMultipleAlignmentBlock();
+			pad.setReferenceId(getReferenceId());
+			AlignedSequence ref = new AlignedSequence(getReference().getContainingSequenceId());
+			ref.setStart(start);
+			ref.setEnd(getReferenceStart());
+			ref.setId(getReferenceId());
+			pad.addAlignedSequence(ref);
+			
+
+			ref.setSequenceBases(buildPad(startPadLength));
+			addBlock(pad);
+		}
+		
+		if(endPadLength > 0) {
+			MAFMultipleAlignmentBlock pad = new MAFMultipleAlignmentBlock();
+			pad.setReferenceId(getReferenceId());
+			AlignedSequence ref = new AlignedSequence(getReference().getContainingSequenceId());
+			ref.setStart(getReferenceEnd() + 1);
+			ref.setEnd(end);
+			ref.setId(getReferenceId());
+			pad.addAlignedSequence(ref);
+			
+
+			ref.setSequenceBases(buildPad(endPadLength));
+			addBlock(pad);
+		}
+
+	}
+	
+	private String buildPad(int size) {
+		StringBuilder padSeq = new StringBuilder(size );
+		for(int i = 0; i <  size; i++) {
+			padSeq.append("N");
+		}
+		
+		return padSeq.toString();
+	}
+	public static class MAFMultipleAlignmentBlock extends MultipleAlignment {
+		int pass;
+		
+		public MAFMultipleAlignmentBlock() {
+			super();
+			setRefGapped(true);
+		}
+		
+		public MAFMultipleAlignmentBlock(MultipleAlignment ma) {
+			this();
+			setReferenceId(ma.getReferenceId());
+			Iterator<AlignedSequence> it = ma.getAlignedSequences().iterator();
+			while(it.hasNext()) {
+				addAlignedSequence(it.next());
+			}
+			
+		}
+
+		public int getPass() { return pass;}
+
+		
+		public AlignedSequence createSequenceFromRawData(String[] data) {		
+			String [] seqNameInfo = data[0].split("\\.");
+			AlignedSequence aln = new AlignedSequence(seqNameInfo[0].intern());
+			if(seqNameInfo.length > 1) {
+				aln.setChromosome(seqNameInfo[1]);
+			}
+			aln.setId(seqNameInfo[0]);
+			aln.setName(seqNameInfo[0]);
+			aln.setRegionStart(Integer.parseInt(data[1]));
+			aln.setRegionEnd(aln.getRegionStart() + Integer.parseInt(data[2]));
+			aln.setStrand(data[3]);
+			aln.setTotalLength(Integer.parseInt(data[4]));
+			aln.setSequenceBases(data[5]);
+			return aln;			
+		}
+		
+		public AlignedSequence addSequenceFromRawData(String[] data) {
+			AlignedSequence aln = createSequenceFromRawData(data);
+			addSequence(aln);
+			return aln;
+		}
+		
+		public void addAlignedSequence(AlignedSequence aln) {
+			addSequence(aln);
+		}
+		
+		public AlignedSequence getAlignedSequence(String sequenceId) {
+			AlignedSequence seq = super.getAlignedSequence(sequenceId);
+			if(seq == null) {
+				seq = new AlignedSequence(sequenceId);
+				int length = length();
+				for(int i = 0; i < length; i++) {
+					seq.appendToSequence('-');
+				}
+			}
+			
+			return seq;
+		}
+		
+		public void setAlignmentInfoFromRawData(String[] data) {
+			for(int i = 0; i < data.length; i++) {
+				String[] nameValPair = data[i].split("=");
+				if(nameValPair[0].equalsIgnoreCase("pass")) {
+					setPass(Integer.parseInt(nameValPair[1]));
+				} else if(nameValPair[0].equalsIgnoreCase("score")) {
+					setScore(Float.parseFloat(nameValPair[1]));
+				} else {
+					System.err.println("Unsuported alignment attribute: " + nameValPair[0] + " .... ignoring");
+				}
+			}
+		}
+		
+		public void setPass(int pass) { this.pass = pass;}
+		
+		public MAFMultipleAlignmentBlock trim(GenomicAnnotation target) {
+			//System.out.println("target: " + target.getLocationString() + " reference: " + getReference().getLocationString());
+			if(target.getStart() <= getReferenceStart() && target.getEnd() >= getReferenceEnd()) {
+				//System.out.println("No trim is nencesary");
+				return this;
+			}
+			
+			return new MAFMultipleAlignmentBlock(getSubAlignment(target.getStart(), target.getEnd(), false));
+			
+		}
+		
+		
+	}
+	
+	public static class MAFHeader {
+		MAFAlignment alignment;
+		String version;
+		String scoring;
+		String program;
+		
+		String runParameters;
+		
+		Hashtable<String, String> otherVariables = new Hashtable<String, String>();
+		
+		private static final long serialVersionUID = 239451013421586L;
+
+		protected void setVariablesFromRawData(String [] data) {
+			for(int i = 0; i < data.length; i++) {
+				String [] variableValuePair = data[i].split("=");
+				if(variableValuePair[0].equalsIgnoreCase("version")) {
+					version = variableValuePair[1];
+				} else if (variableValuePair[0].equalsIgnoreCase("scoring")) {
+					scoring = variableValuePair[1];
+				} else if (variableValuePair[0].equalsIgnoreCase("program")) {
+					program = variableValuePair[1];
+				} else {
+					otherVariables.put(variableValuePair[0].toLowerCase(), variableValuePair[1]);
+				}
+				
+			}
+		}
+		
+		public String getRunParameters() {
+			return runParameters;
+		}
+
+		public void addVariableValuePair(String variable, String value) {
+			otherVariables.put(variable, value);
+		}
+		public void write(BufferedWriter bw) throws IOException {
+			bw.write("##maf");
+			if(version != null && version.length() > 0) {
+				bw.write(" version=");
+			    bw.write(version);
+			}
+			if(scoring != null && scoring.length() > 0) {
+				bw.write(" scoring=");
+				bw.write(scoring);
+			}
+			if(program != null && program.length() > 0) {
+				bw.write(" program=");
+				bw.write(program);
+			}
+			
+			Iterator<String> varIt = otherVariables.keySet().iterator();
+			while(varIt.hasNext()) {
+				String var = varIt.next();
+				bw.write(" ");
+				bw.write(var);
+				bw.write("=");
+				bw.write(otherVariables.get(var));
+			}
+			bw.newLine();
+			
+			if(runParameters != null) {
+				bw.write("# ");
+				bw.write(runParameters);
+			}
+
+			bw.newLine();
+			bw.newLine();
+		}
+
+		public void setRunParameters(String runParameters) {
+			this.runParameters = runParameters;
+		}
+
+
+
+		public void setProgram(String program) {
+			this.program = program;
+		}
+
+		public void setScoring(String scoring) {
+			this.scoring = scoring;
+		}
+
+		public void setVersion(String version) {
+			this.version = version;
+		}
+
+		public String getProgram() {
+			return program;
+		}
+
+		public String getScoring() {
+			return scoring;
+		}
+
+		public String getVersion() {
+			return version;
+		}
+		
+	}
+	
+	protected void setAlignedSequences(List<String> seqIds) {
+		sequenceIds = seqIds;
+	}
+
+	public void loadIndex(String idxFile) throws IOException {
+		index = new IntervalTree<Long>();//new LinkedHashMap<Integer, Long>();;
+		BufferedReader br = new BufferedReader(new FileReader(idxFile));
+		//System.err.println("Loading index " + idxFile);
+		String line = null;
+		int l = 0;
+		while((line = br.readLine()) != null) {
+			String [] info = line.split("\t");
+			int start = Integer.parseInt(info[0]);
+			int end   = Integer.parseInt(info[1]) + start;
+			long offset = Long.parseLong(info[2]);
+			//System.out.println("Read line "+ l++);
+			index.put(start, end, offset);
+
+		}
+		
+	}
+	
+	private Map<String, Short> getGapColumn() {
+		HashMap<String, Short> gapCol = new HashMap<String, Short>(sequenceIds.size());
+		Iterator<String> sequenceIdIt = sequenceIds.iterator();
+		while(sequenceIdIt.hasNext()) {
+			gapCol.put(sequenceIdIt.next(), GAP_CODE);
+		}
+		
+		return gapCol;
+	}
+
+}
diff --git a/src/edu/mit/broad/prodinfo/multiplealignment/MAFIO.java b/src/edu/mit/broad/prodinfo/multiplealignment/MAFIO.java
new file mode 100644
index 0000000..e4ba31d
--- /dev/null
+++ b/src/edu/mit/broad/prodinfo/multiplealignment/MAFIO.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package edu.mit.broad.prodinfo.multiplealignment;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.RandomAccessFile;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Stack;
+
+import edu.mit.broad.prodinfo.genomicplot.ParseException;
+import edu.mit.broad.prodinfo.multiplealignment.MAFAlignment.MAFHeader;
+import edu.mit.broad.prodinfo.multiplealignment.MAFAlignment.MAFMultipleAlignmentBlock;
+import edu.mit.broad.prodinfo.multiplealignment.MultipleAlignment.AlignedSequence;
+
+public class MAFIO implements MultipleAlignmentIO {
+	RandomAccessFile fileHandle;
+	MAFAlignment alignment;
+	String alignmentFile;
+
+	public MAFIO(String alignmentFile, boolean keepFileHandle) throws IOException, ParseException {
+		alignment = createUnloadedAlignment(alignmentFile);
+		this.alignmentFile = alignmentFile;
+		if(keepFileHandle) {
+			fileHandle = new RandomAccessFile(alignmentFile, "r");
+		}
+	}
+
+	public MAFIO() {
+		super();
+	}
+	public String getPreferredFileExtension() {
+		return ".maf";
+	}
+
+	public void destroyFileHandle() throws IOException {
+        if(fileHandle != null) {
+            fileHandle.close();
+        }
+    }
+
+	public MAFAlignment load(List<String> sequencesToLoad, int start, int end) throws IOException, ParseException {
+		//RandomAccessFile handle = fileHandle;
+		boolean closeFile = false;
+		if(fileHandle == null ) { // It would be nice to test whether the handle is closed if not null....
+            closeFile = true;
+			fileHandle = new RandomAccessFile(alignmentFile, "r");
+		}
+		try {
+			alignment.load(fileHandle, start, end, sequencesToLoad);
+		} finally {
+			if(closeFile) {
+                fileHandle.close();
+                fileHandle = null;
+            }
+		}
+		return alignment;
+	}
+
+	public MAFAlignment createUnloadedAlignment(String fileName) throws IOException, ParseException {
+		MAFAlignment aln = new MAFAlignment();
+		String idxFileName = fileName + ".index";
+		File idxFile = new File(idxFileName);
+		if(!idxFile.exists()) {
+			aln.createIndex(fileName);
+			aln.writeIndex(idxFileName);
+		} else {
+			aln = new MAFAlignment(idxFileName);
+		}
+
+		return aln;
+	}
+
+	public MAFAlignment load(String fileName) throws IOException, ParseException {
+		return load(fileName, new ArrayList<String>());
+	}
+
+	public MAFAlignment load(String fileName, List<String> sequencesToLoad) throws IOException, ParseException {
+		MAFAlignment aln = new MAFAlignment();
+		String idxFileName = fileName + ".index";
+		File idxFile = new File(idxFileName);
+		if(!idxFile.exists()) {
+			aln.createIndex(fileName);
+			aln.writeIndex(idxFileName);
+		} else {
+			aln = new MAFAlignment(idxFileName);
+		}
+
+		RandomAccessFile raf = new RandomAccessFile(fileName,"r");
+		aln.load(raf, sequencesToLoad);
+		raf.close();
+		return aln;
+	}
+
+	public MultipleAlignment load(InputStream in) throws IOException, ParseException {
+		return load(in, new ArrayList<String>());
+	}
+
+	public MAFAlignment load(String fileName, List<String> sequencesToLoad, int start, int end) throws IOException, ParseException {
+		MAFAlignment aln = new MAFAlignment();
+		String idxFileName = fileName + ".index";
+		File idxFile = new File(idxFileName);
+		if(!idxFile.exists()) {
+			System.out.print("Index file not exists, creating and writing it: " + idxFileName);
+			aln.createIndex(fileName);
+			aln.writeIndex(idxFileName);
+			System.out.println("   Done writing index");
+		} else {
+			aln = new MAFAlignment(idxFileName);
+		}
+
+		RandomAccessFile raf = new RandomAccessFile(fileName,"r");
+		aln.load(raf,start, end,sequencesToLoad);
+		raf.close();
+		return aln;
+	}
+
+	public MultipleAlignment load(InputStream in, List<String> sequencesToLoad) throws IOException, ParseException {
+		MAFAlignment aln = new MAFAlignment();
+		MAFHeader header = aln.getHeader();
+		Stack<MAFMultipleAlignmentBlock> blocks = new Stack<MAFMultipleAlignmentBlock>();
+		ArrayList<String> seqIds = new ArrayList<String>();
+
+		BufferedReader br = new BufferedReader(new InputStreamReader(in));
+		String [] lineInfo = null;
+		String line = br.readLine();
+		if(line == null || !line.startsWith("##maf ")) {
+			throw new ParseException("First line in the file " + line + " was either null or did not start with \"##maf \" as specified");
+		}
+
+		header.setVariablesFromRawData(line.substring(6).split("\\s"));
+
+		line = br.readLine();
+		if(line == null || !line.startsWith("# ")) {
+			throw new ParseException("Second line in the file " + line + " was either null or did not start with \"# \" as specified");
+		}
+		header.setRunParameters(line.substring(2));
+
+		while((line = br.readLine()) != null) {
+			//Ignore all comment lines
+			if(line.startsWith("#") || line.trim().length() == 0){
+				continue;
+			}
+
+			if(line.startsWith("a ")) {
+				MAFMultipleAlignmentBlock ma = new MAFMultipleAlignmentBlock();
+				blocks.push(ma);
+				lineInfo = line.substring(2).split("\\s");
+				ma.setAlignmentInfoFromRawData(lineInfo);
+			} else if(line.startsWith("s ")) {
+				//System.out.println("\tAlignment aligned seq " + line);
+				MAFMultipleAlignmentBlock ma = blocks.peek();
+				lineInfo = line.substring(2).split("\\s+");
+				AlignedSequence alnSeq = ma.addSequenceFromRawData(lineInfo);
+				if(sequencesToLoad !=null && sequencesToLoad.size() > 0 && !sequencesToLoad.contains(alnSeq.getId()) ) {
+					//System.out.println("sequence " + alnSeq.getId() + " is not in sequencesToLoad, ignoring");
+					continue;
+				}
+
+				//System.out.println("ma  getReferenceId? " + ma.getReferenceId());
+
+				if(aln.getReferenceId() == null) {
+					System.err.println("Ref is " + ma.getReferenceId());
+					aln.setReferenceId(alnSeq.getId());
+				}
+
+				if(ma.getReferenceId() != null) {
+					ma.setReferenceId(aln.getReferenceId());
+				}
+
+				if(!seqIds.contains(alnSeq.getId())) {
+					seqIds.add(alnSeq.getId());
+				}
+			} else if (line.startsWith("i ") || line.startsWith("e ")) {
+				System.err.println("Information blocks (i/e) are not yet supported");
+			} else {
+				throw new ParseException("Invalid alignment line <"+ line +">");
+			}
+
+		}
+
+		aln.setBlocks(blocks);
+		aln.setAlignedSequences(seqIds);
+		return aln.toMultipleAlignment();
+	}
+
+	public void write(BufferedWriter bw, MultipleAlignment ma)
+			throws IOException {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void write(BufferedWriter bw, MultipleAlignment ma, List<String> sequenceOrder)
+		throws IOException {
+		// TODO Auto-generated method stub
+	}
+
+
+}
diff --git a/src/edu/mit/broad/prodinfo/multiplealignment/MultipleAlignment.java b/src/edu/mit/broad/prodinfo/multiplealignment/MultipleAlignment.java
new file mode 100644
index 0000000..02eafa5
--- /dev/null
+++ b/src/edu/mit/broad/prodinfo/multiplealignment/MultipleAlignment.java
@@ -0,0 +1,736 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package edu.mit.broad.prodinfo.multiplealignment;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Stack;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import Jama.Matrix;
+import edu.mit.broad.prodinfo.chromosome.BasicGenomicAnnotation;
+import edu.mit.broad.prodinfo.genomicplot.GenomicAnnotation;
+import edu.mit.broad.prodinfo.sequence.Sequence;
+import edu.mit.broad.prodinfo.sequence.SequenceRegion;
+
+public class MultipleAlignment  {
+	private float score;
+	private LinkedHashMap<String, AlignedSequence> alignments;
+
+	private MultipleAlignmentIO ioHelper;
+	public static final short A_CODE = Sequence.SHORT_ENCODED_A;
+	public static final short C_CODE = Sequence.SHORT_ENCODED_C;
+	public static final short G_CODE = Sequence.SHORT_ENCODED_G;
+	public static final short T_CODE = Sequence.SHORT_ENCODED_T;
+	public static final short GAP_CODE = Sequence.SHORT_ENCODED_GAP;
+
+	protected static int UNGAPPED_ALPHABET_SIZE = 4;
+
+	private String referenceId;
+	private boolean isRefGapped;
+
+	public MultipleAlignment() {
+		alignments = new LinkedHashMap<String, AlignedSequence>();
+	}
+
+	/**
+	 * Constructs a multiple alignment object out of piecemeal multiple alignment blocks with, possible,
+	 * different number of aligned sequences.
+	 * @param exonAlnBlocks The blocks to combined into a single multiple alignment block
+	 * @param string reference sequence id
+	 * @param seqs array of sequence ids, the end alignment will include all even in the cases where a sequence alignment is gap only.
+	 */
+	public MultipleAlignment(List<? extends MultipleAlignment> alnBlocks, String ref, String[] seqs) {
+		//TODO: Implement.
+	}
+
+	public void encode() {
+		Iterator<AlignedSequence> seqIt = alignments.values().iterator();
+		while(seqIt.hasNext()) {
+			AlignedSequence as = seqIt.next();
+			as.encodeSequenceIgnoreCase();
+			as.unloadSequence();
+		}
+
+	}
+
+	public void encodeAsMatrix() {
+		Iterator<AlignedSequence> seqIt = alignments.values().iterator();
+		while(seqIt.hasNext()) {
+			AlignedSequence as = seqIt.next();
+			as.encodeSequenceAsVector();
+			as.unloadSequence();
+		}
+	}
+
+	public void reverse() {
+		Iterator<AlignedSequence> seqIt = alignments.values().iterator();
+		while(seqIt.hasNext()) {
+			AlignedSequence as = seqIt.next();
+			as.reverse();
+		}
+
+	}
+	public int length() {
+		return alignments.values().iterator().next().getLength();
+	}
+
+	/**
+	 * Assumes that encode has been called and each aligned sequence is encoded
+	 * @param int - alignment column
+	 */
+	public Map<String, Short> getColumn(int i) {
+		LinkedHashMap<String, Short> col = new LinkedHashMap<String, Short>(getAlignedSequenceIds().size());
+		Iterator<String> seqIdIt = getAlignedSequenceIds().iterator();
+		while(seqIdIt.hasNext()) {
+			String seqId = seqIdIt.next();
+			col.put(seqId, alignments.get(seqId).getEncodedSequence()[i - getReferenceStart()]);
+		}
+		return col;
+	}
+
+	public Map<String, Matrix> getColumnsAsVector(int start, int number) throws ArrayIndexOutOfBoundsException{
+		LinkedHashMap<String, Matrix> cols = new LinkedHashMap<String, Matrix>(getAlignedSequenceIds().size());
+		Iterator<String> seqIdIt = getAlignedSequenceIds().iterator();
+		start = start - getReferenceStart();
+		//System.out.println("start: " + start + " ref-start " + getReferenceStart());
+		while(seqIdIt.hasNext()) {
+			String seqId = seqIdIt.next();
+			short [] alignedSeqSeq = alignments.get(seqId).getEncodedSequence();
+			Matrix alignmentMatrix = alignments.get(seqId).getVectorEncodedSequence();
+
+			Matrix seqRegion = new Matrix(UNGAPPED_ALPHABET_SIZE, number);
+			for(int j = start; j < start + number; j++) {
+				if(alignedSeqSeq != null ) {
+					if(alignedSeqSeq[j] < UNGAPPED_ALPHABET_SIZE) { // gapped sequence have a 0 column.
+						seqRegion.set(alignedSeqSeq[j], j - start, 1);
+					}
+				} else if(alignmentMatrix != null) {
+					for(int i = 0; i < alignmentMatrix.getRowDimension(); i++) {
+						seqRegion.set(i, j - start , alignmentMatrix.get(i,j));
+					}
+				}
+			}
+			cols.put(seqId, seqRegion);
+		}
+		return cols;
+	}
+
+	public Map<String, Matrix> getAsMatrix() {
+		LinkedHashMap<String, Matrix> matrixAlignment = new  LinkedHashMap<String, Matrix>(getAlignedSequenceIds().size());
+		Iterator<String> seqIdIt = getAlignedSequenceIds().iterator();
+		while(seqIdIt.hasNext()) {
+			String seqId = seqIdIt.next();
+			matrixAlignment.put(seqId, getAlignedSequence(seqId).getVectorEncodedSequence());
+		}
+		return matrixAlignment;
+	}
+
+	public void addShortEncodedColumn(Map<String, Short> col) {
+		Iterator<String> it = col.keySet().iterator();
+		while(it.hasNext()) {
+			String seqId = it.next();
+			AlignedSequence seq = getAlignedSequence(seqId);
+			if(seq == null) {
+				seq = new AlignedSequence(seqId);
+				seq.setId(seqId);
+				addSequence(seq);
+			}
+			seq.appendToSequence(decodeShort(col.get(seqId)));
+		}
+
+	}
+
+	public void addShortEncodedRegion(Map<String, short[]> region) {
+		if(region == null || region.isEmpty()) {
+			return;
+		}
+
+		int cols = region.values().iterator().next().length;
+		for(int i = 0; i < cols; i++) {
+			Iterator<String> it = region.keySet().iterator();
+			while(it.hasNext()) {
+				String seqId = it.next();
+				AlignedSequence seq = getAlignedSequence(seqId);
+				if(seq == null) {
+					seq = new AlignedSequence(seqId);
+					seq.setId(seqId);
+					addSequence(seq);
+				}
+				seq.appendToSequence(decodeShort(region.get(seqId)[i]));
+			}
+		}
+
+	}
+
+
+	public char decodeShort(Short nucleotide) {
+		char decoded = ' ';
+		switch (nucleotide) {
+		case A_CODE : decoded = 'A'; break;
+		case C_CODE : decoded = 'C'; break;
+		case G_CODE : decoded = 'G'; break;
+		case T_CODE : decoded = 'T'; break;
+		default : decoded = '-';
+		}
+		return decoded;
+	}
+
+	public void addSequence(AlignedSequence seq) {
+		if(seq == null) {
+			throw new IllegalArgumentException("Trying to add a null sequence");
+		}
+		if(alignments.keySet().contains(seq.getContainingSequenceId())) {
+			throw new IllegalArgumentException("Sequence " + seq.getContainingSequenceId() + " has already been added");
+		}
+
+		alignments.put(seq.getContainingSequenceId(), seq);
+	}
+
+	public void addSequences(List<AlignedSequence> sequences) {
+		Iterator<AlignedSequence> it = sequences.iterator();
+		while(it.hasNext()) {
+			addSequence(it.next());
+		}
+	}
+
+	public boolean isEmpty() { return alignments.isEmpty();}
+
+	public void setIOHelper(MultipleAlignmentIO maio) { this.ioHelper = maio;}
+	protected MultipleAlignmentIO getIOHelper() { return ioHelper;}
+
+	public void write(BufferedWriter bw) throws IOException {
+		if(ioHelper == null) {
+			throw new IllegalStateException("The MultipleAlignmentIO (ioHelper) is null set it first");
+		}
+		ioHelper.write(bw, this);
+	}
+
+	public void write(BufferedWriter bw, List<String> order) throws IOException {
+		if(ioHelper == null) {
+			throw new IllegalStateException("The MultipleAlignmentIO (ioHelper) is null set it first");
+		}
+		ioHelper.write(bw, this, order);
+	}
+
+	public MultipleAlignment sampleColumns(int colNum, int numConsecutiveCols) {
+		Random r = new Random();
+		LinkedHashMap<String, List<Character>> sequenceCharacters = new LinkedHashMap<String, List<Character>>(getAlignedSequenceIds().size());
+
+		Iterator<String> seqIdIt = getAlignedSequenceIds().iterator();
+
+		MultipleAlignment sampled = new MultipleAlignment();
+
+		while(seqIdIt.hasNext()) {
+			String seq = seqIdIt.next();
+			AlignedSequence alignedSeq = getAlignedSequence(seq);
+			char [] seqBasesArray = alignedSeq.getSequenceBases().toCharArray();
+			List<Character> seqChars = new ArrayList<Character>(seqBasesArray.length);
+
+			sequenceCharacters.put(seq, seqChars);
+			for(int i = 0; i < seqBasesArray.length; i++) {
+				seqChars.add(seqBasesArray[i]);
+			}
+			//alignedSeq.unloadSequence();
+			AlignedSequence newSeq = new AlignedSequence(alignedSeq);
+			newSeq.unloadSequence();
+			sampled.addSequence(newSeq);
+		}
+
+		int length = sequenceCharacters.values().iterator().next().size();
+		sampled.setReferenceId(getReferenceId());
+
+		for(int i = 0; i < colNum; i++) {
+			int col = r.nextInt(length - numConsecutiveCols + 1);
+			//System.out.println("Sampled col " + col + " out of " + length + " cols");
+			seqIdIt = getAlignedSequenceIds().iterator();
+			while(seqIdIt.hasNext()) {
+				String seqId = seqIdIt.next();
+				AlignedSequence seq = sampled.getAlignedSequence(seqId);
+				List<Character> seqChars = sequenceCharacters.get(seqId);
+				for(int j = 0; j < numConsecutiveCols; j++) {
+					seq.appendToSequence(seqChars.get(col + j));
+				}
+			}
+		}
+		//System.out.println(getReferenceId() + " bases: " + sampled.getAlignedSequence(getReferenceId()).getSequenceBases());
+		return sampled;
+	}
+
+	public void permuteColumns() {
+		Random r = new Random();
+		LinkedHashMap<String, List<Character>> sequenceCharacters = new LinkedHashMap<String, List<Character>>(getAlignedSequenceIds().size());
+
+		Iterator<String> seqIdIt = getAlignedSequenceIds().iterator();
+
+		while(seqIdIt.hasNext()) {
+			String seq = seqIdIt.next();
+			AlignedSequence alignedSeq = getAlignedSequence(seq);
+			char [] seqBasesArray = alignedSeq.getSequenceBases().toCharArray();
+			List<Character> seqChars = new ArrayList<Character>(seqBasesArray.length);
+
+			sequenceCharacters.put(seq, seqChars);
+			for(int i = 0; i < seqBasesArray.length; i++) {
+				seqChars.add(seqBasesArray[i]);
+			}
+			alignedSeq.unloadSequence();
+		}
+
+		int length = sequenceCharacters.values().iterator().next().size();
+		while (length > 0) {
+			int pos = r.nextInt(length);
+			seqIdIt = alignments.keySet().iterator();
+
+			while(seqIdIt.hasNext()) {
+				String seqId = seqIdIt.next();
+				AlignedSequence seq = alignments.get(seqId);
+				List<Character> seqChars = sequenceCharacters.get(seqId);
+				seq.appendToSequence(seqChars.remove(pos));
+			}
+			length--;
+		}
+	}
+
+	public float getScore() { return score;}
+	public void setScore(float score) { this.score = score;}
+
+	public AlignedSequence getAlignedSequence(String containingSequenceId) { return alignments.get(containingSequenceId); }
+
+	public List<AlignedSequence> getAlignedSequences() { return new ArrayList<AlignedSequence>(alignments.values());}
+
+	public List<String> getAlignedSequenceIds() { return new ArrayList<String>(alignments.keySet());}
+
+	public boolean overlaps(SequenceRegion region) {
+		boolean overlap = false;
+		AlignedSequence seq = alignments.get(region.getContainingSequenceId());
+		if (seq != null) {
+			overlap = seq.overlaps(region);
+		}
+		return overlap;
+	}
+
+	public String getReferenceId() {
+		String refId = null;
+		if(referenceId != null) {
+			refId = referenceId;
+		} else if (!alignments.isEmpty()){
+			refId =  alignments.keySet().iterator().next() ;
+		}
+		return refId;
+	}
+
+	public void setReferenceId(String referenceId) {
+		this.referenceId = referenceId.intern();
+	}
+
+	public int getReferenceStart() {
+		AlignedSequence refChunk = getReference();
+		return refChunk == null ? -1 : refChunk.getStart();
+	}
+
+	public AlignedSequence getReference() {
+		AlignedSequence reference = null;
+		if(referenceId != null) {
+			reference = getAlignedSequence(referenceId);
+		} else if(alignments.size() > 0) {
+			reference = alignments.values().iterator().next();
+		}
+
+		return reference;
+	}
+
+	public int getReferenceEnd() {
+		AlignedSequence refChunk = getReference();
+		return refChunk == null ? -1 : refChunk.getEnd();
+	}
+
+	public boolean isRefGapped() {
+		return isRefGapped;
+	}
+
+	public void setRefGapped(boolean isRefGapped) {
+		this.isRefGapped = isRefGapped;
+	}
+
+	protected Iterator<AlignedSequence> getAlignedSequences(String refId, int refStart, int refEnd, boolean reverse) {
+		ArrayList<AlignedSequence> seqs = new ArrayList<AlignedSequence>(alignments.size());
+
+		return seqs.iterator();
+	}
+
+	public MultipleAlignment getConcatenatedSubAlignment(String refId, List<? extends GenomicAnnotation> annotations) {
+		MultipleAlignment result = new MultipleAlignment();
+		result.setIOHelper(this.ioHelper);
+		Iterator<? extends GenomicAnnotation> it = annotations.iterator();
+		GenomicAnnotation ga = null;
+		while(it.hasNext()) {
+			ga = it.next();
+			result.append(getSubAlignment(refId, ga.getStart(), ga.getEnd(), ga.inReversedOrientation()));
+		}
+		return result;
+	}
+
+	/**
+	 * Compresses alignment by removing columns containing reference gaps
+	 */
+	public void compress() {
+		HashMap<String, StringBuilder> compressSequenceMap = new HashMap<String, StringBuilder>(getAlignedSequenceIds().size());
+		List<int []> ungappedRefIslands = getUngappedReferenceIslands();
+		Iterator<int []> refIslandIt = ungappedRefIslands.iterator();
+		while(refIslandIt.hasNext()) {
+			Iterator<AlignedSequence> seqIt = getAlignedSequences().iterator();
+			int [] ungappedIsland = refIslandIt.next();
+			while(seqIt.hasNext()) {
+				AlignedSequence seq = seqIt.next();
+				StringBuilder compressedSeqBuilder = compressSequenceMap.get(seq.getId());
+				if(compressedSeqBuilder == null) {
+					compressedSeqBuilder = new StringBuilder();
+					compressSequenceMap.put(seq.getId(), compressedSeqBuilder);
+				}
+				compressedSeqBuilder.append(seq.getSequenceBases().substring(ungappedIsland[0], ungappedIsland[1]));
+			}
+		}
+
+		Iterator<String> seqIdIt = compressSequenceMap.keySet().iterator();
+		while(seqIdIt.hasNext()) {
+			String seqId = seqIdIt.next();
+			AlignedSequence seq = getAlignedSequence(seqId);
+			seq.setSequenceBases(compressSequenceMap.get(seqId).toString());
+		}
+		isRefGapped = false;
+	}
+
+	public void remove(List<String> removeList) {
+		Iterator<String> it = removeList.iterator();
+
+		while(it.hasNext()) {
+			String toRemove = it.next();
+			System.out.print("To remove " + toRemove + " ... ");
+			AlignedSequence removedSeq = alignments.remove(toRemove);
+			System.out.println(removedSeq == null ? " not found " : "yes");
+		}
+	}
+
+	public MultipleAlignment getSubAlignment(int refStart, int refEnd, boolean reverse) {
+		return getSubAlignment(getReferenceId(), refStart, refEnd, reverse);
+	}
+
+	/**
+	 * Computes the number of sequences that have aligning bases as opposed to being pure gap
+	 * @return Number of sequences that are not gap only.
+	 */
+	public int getNumberOfAligningSequences() {
+		int i = 0;
+		Iterator<AlignedSequence> seqIt = getAlignedSequences().iterator();
+		while(seqIt.hasNext()) {
+			AlignedSequence seq = seqIt.next();
+			if (seq.getUngappedLength() > 0 ) {
+				i++;
+			}
+		}
+
+		return i;
+	}
+
+	/**
+	 *
+	 * @param refId - The sequence id in which coordinates are given
+	 * @param refStart - Start of region of interest in the refId sequence. Positions start in 1.
+	 * @param refEnd - End of region of interest in the refId sequence. Positions start in 1.
+	 * @param reverse - If true the multiple alignment will return all sequences in the opposite strand
+	 * @return The multiple alignment of the region of interest or an empty alignment if the coordinates
+	 * 			do not overlap the refId aligned region.
+	 */
+	public MultipleAlignment getSubAlignment(String refId, int refStart, int refEnd, boolean reverse) {
+		MultipleAlignment result = new MultipleAlignment();
+		BasicGenomicAnnotation target = new BasicGenomicAnnotation("ReferenceTarget");
+		target.setStart(refStart);
+		target.setEnd(refEnd);
+
+		AlignedSequence ref = alignments.get(refId) ;
+		if(ref == null) {
+			throw new IllegalArgumentException("Bad refId, multiple alignment does not include " + refId);
+		}
+		target.takeIntersection(ref);
+		//System.out.println("RefStart " + refStart + " ref.getRegionStart() " + ref.getRegionStart() + " refEnd " + refEnd );
+		int stringStartPos = ref.getGapAdjustedCoordinate(target.getStart() - ref.getRegionStart());
+		int stringEndPos   = ref.getGapAdjustedCoordinate(target.getEnd()   - ref.getRegionStart());
+		//System.out.println("gap adjusted start " + stringStartPos + " gap adjusted end " +stringEndPos );
+
+		Iterator<AlignedSequence> it = getAlignedSequences().iterator();
+		SequenceRegion tmpRegion = null;
+		while(it.hasNext()) {
+			AlignedSequence seq = it.next();
+			//System.out.print(seq.getId());
+			tmpRegion = seq.getRegion(stringStartPos, stringEndPos);
+			//System.out.println(" " + tmpRegion.getSequenceBases());
+			if(reverse){
+				tmpRegion.reverse();
+			}
+			AlignedSequence newSeq = new AlignedSequence(tmpRegion.getContainingSequenceId());
+			newSeq.setRegionStart(seq.getRegionStart() + seq.getSequencePosition(stringStartPos));
+			newSeq.setRegionEnd(seq.getRegionStart() + seq.getSequencePosition(stringEndPos));
+			newSeq.setChromosome(seq.getChromosome());
+			newSeq.setSequence(tmpRegion);
+			newSeq.setName(tmpRegion.getContainingSequenceId());
+			result.addSequence(newSeq);
+		}
+
+		result.setIOHelper(this.ioHelper);
+		return result;
+
+	}
+	public void append(MultipleAlignment other) {
+		if(alignments.isEmpty()) {
+			Iterator<AlignedSequence> otherSeqIt =  other.getAlignedSequences().iterator();
+			//System.out.println("ailignments are empty, setting to others");
+			while(otherSeqIt.hasNext()) {
+				AlignedSequence seq = otherSeqIt.next();
+				AlignedSequence seqClone = new AlignedSequence(seq);
+				alignments.put(seq.getId(), seqClone);
+			}
+		} else {
+			//Iterator<AlignedSequence> it = other.getAlignedSequences().iterator();
+			Iterator<AlignedSequence> it = getAlignedSequences().iterator();
+			AlignedSequence otherSeq = null;
+			AlignedSequence thisSeq = null;
+			while(it.hasNext()) {
+				thisSeq = it.next();
+				//System.out.println("This seq " + thisSeq.getId() + " bases " + thisSeq.getSequenceBases());
+				otherSeq  = other.getAlignedSequence(thisSeq.getName());
+				//System.out.println("Other seq " + otherSeq.getId() + " bases " + otherSeq.getSequenceBases());
+				if (otherSeq == null) {
+					throw new IllegalStateException("Sequence " + thisSeq.getName() +" has no entry in other alignment, can't append alignment");
+				}
+				thisSeq.appendToSequence(otherSeq.getSequenceBases());
+				thisSeq.setEnd(thisSeq.getEnd() + otherSeq.getUngappedLength());
+			}
+		}
+	}
+
+	public static class AlignedSequence extends SequenceRegion {
+		private static final Pattern GAP_PATTERN = Pattern.compile("-+");
+		private static final Pattern UNGAP_PATTERN = Pattern.compile("[^-]+");
+		private int totalLength;
+		Stack<int[]> ungappedRegions;
+
+		public AlignedSequence(Sequence sequence) {
+			super(sequence.getId());
+			setId(sequence.getId());
+			setName(sequence.getId());
+			setSequence(sequence);
+			setRegionStart(0);
+			setRegionEnd(getLength());
+			setSequenceBases(getSequenceBases());
+
+		}
+
+		public void setSequenceBases(String bases ) {
+			super.setSequenceBases(bases);
+		}
+
+		public AlignedSequence(String containingSequenceId) {
+			super(containingSequenceId);
+			setId(containingSequenceId);
+		}
+
+		public int getLength() {
+			if(getEncodedSequence() != null && getEncodedSequence().length > 0) {
+				return getEncodedSequence().length;
+			} else if (getVectorEncodedSequence() != null) {
+				return getVectorEncodedSequence().getColumnDimension();
+			} else if (getSequenceBases() != null && getSequenceBases().length() > 0) {
+				return getSequenceBases().length();
+			}else {
+				return super.getLength();
+			}
+		}
+
+		public int getUngappedLength() {
+			int ungappedLength = 0;
+			char [] basesArr = getSequenceBases().toCharArray();
+			int gaps = 0;
+			for(int i = 0; i < basesArr.length; i++) {
+				if(basesArr[i] == '-') {
+					gaps++;
+				}
+
+			}
+			ungappedLength = getLength() - gaps;
+			return ungappedLength;
+		}
+
+
+		public void setStrand(String strand) { super.setForwardStrand("-".equals(strand)); }
+
+		public float getPercentGaps() {
+			return 1f - getUngappedLength()/(float)getLength();
+		}
+
+		public int getTotalGaps() { return getLength() - getUngappedLength();}
+
+		public List<Integer> getGapSizes() {
+			Matcher m = GAP_PATTERN.matcher(getSequenceBases());
+			ArrayList<Integer> gapSizes = new ArrayList<Integer>();
+			while(m.find()) {
+				m.start();
+				m.end();
+				gapSizes.add(m.group().length());
+			}
+			return gapSizes;
+		}
+
+		/**
+		 * Finds the list of ungapped regions in the sequence.
+		 * @return A list of two sized integer arrays with the start and end of the ungapped regions. The convention here is semi closed
+		 * 		   intervals, each list items is of the form [start, end).
+		 */
+		public List<int []> findUngappedChunks() {
+			Matcher m = UNGAP_PATTERN.matcher(getSequenceBases());
+			ArrayList<int []> gapUngapp = new ArrayList<int []>();
+			//System.out.println("Sequence for " + getId() + ": " + getSequenceBases());
+			while(m.find()) {
+				int [] ungappedReg = {m.start(), m.end()};
+				//System.out.println("\treg["+m.start()+"-"+m.end()+"]: " + getSequenceBases().substring(m.start(), m.end()));
+				gapUngapp.add(ungappedReg);
+			}
+			//System.out.println("");
+
+			return gapUngapp;
+		}
+
+		/**
+		 * Maps coordinates relative to start of alignment (0 based) to the gapped adjusted string position in the aligned sequence string.
+		 * @param position to map. It is the inverse of getSequencePostion.
+		 * @return
+		 * @see #getSequencePosition(int)
+		 */
+		public int getGapAdjustedCoordinate(int position) {
+			List<int []> ungappedChunks = findUngappedChunks();
+
+			Iterator<int []> chunkIt = ungappedChunks.iterator();
+			int gapNum = 0;
+			int [] lastChunk = {0,0};
+			while(chunkIt.hasNext()) {
+				int [] chunk = chunkIt.next();
+				int gapsBetweenChunks = chunk[0] - lastChunk[1];
+				if(chunk[0] - gapNum - gapsBetweenChunks <=  position) {
+					gapNum += gapsBetweenChunks;
+				} else {
+					break;
+				}
+				lastChunk = chunk;
+			}
+
+			return position + gapNum;
+		}
+
+		/**
+		 * Maps a string position to a sequence position relative to the start of the alignment. It returns the position once gaps are
+		 * ignored. It is the inverse of getGapAdjustedCoordinate
+		 * @param coordinate
+		 * @return position in sequence.
+		 * @see #getGapAdjustedCoordinate(int);
+		 */
+		public int getSequencePosition(int coordinate) {
+			List<int []> ungappedChunks = findUngappedChunks();
+
+			Iterator<int []> chunkIt = ungappedChunks.iterator();
+			int gapNum = 0;
+			int [] lastChunk = {0,0};
+			while(lastChunk[1] < coordinate && chunkIt.hasNext()) {
+				int [] chunk = chunkIt.next();
+				int gapsBetweenChunks = chunk[0] - Math.min(lastChunk[1], coordinate);
+				if(chunk[0]  <=  coordinate) {
+					gapNum += gapsBetweenChunks;
+				}
+				lastChunk = chunk;
+			}
+
+			return coordinate - gapNum;
+		}
+
+		public int getTotalLength() {
+			return totalLength;
+		}
+
+		public void setTotalLength(int totalLength) {
+			this.totalLength = totalLength;
+		}
+
+
+	}
+
+	/**
+	 * Finds the list of ungapped regions in the reference sequence.
+	 * @return A list of two sized integer arrays with the start and end of the ungapped regions. The convention here is semi closed
+	 * 		   intervals, each list items is of the form [start, end).
+	 */
+	public List<int[]> getUngappedReferenceIslands() {
+		//System.out.println("Reference: " + getReferenceId() + " reference sequence " + getReference());
+		return getReference().findUngappedChunks();
+	}
+
+	/**
+	 * Finds the list of ungapped sequence (as opposed to alignment gaps) regions in the reference sequence.
+	 * @return A list of two sized integer arrays with the start and end of the ungapped regions. The convention here is semi closed
+	 * 		   intervals, each list items is of the form [start, end).
+	 */
+	public List<int[]> getUngappedSequenceReferenceIslands() {
+		//System.out.println("Reference: " + getReferenceId() + " reference sequence " + getReference());
+		return getReference().findUngappedSequenceChunks();
+	}
+
+	public void introduceGaps(boolean canGapReference, int maxNumberOfGaps) throws IllegalAccessException {
+		Random r = new Random();
+
+		int alnLength = length();
+		List<String> seqIds = new ArrayList<String>(getAlignedSequenceIds());
+		if(!canGapReference) {
+			seqIds.remove(getReferenceId());
+		}
+
+		for (int i = 0; i < alnLength; i++) {
+			List<String> availableSeqIds = new ArrayList<String>(seqIds);
+
+			int numOfGaps = r.nextInt(maxNumberOfGaps);
+
+			for(int j = 0; j < numOfGaps; j++) {
+				int toGapIdx = r.nextInt(availableSeqIds.size());
+				String seqToGap = availableSeqIds.remove(toGapIdx);
+				Sequence seq = alignments.get(seqToGap);
+				seq.setCharAt(i, '-');
+			}
+		}
+		
+	}
+
+
+
+
+
+}
diff --git a/src/edu/mit/broad/prodinfo/multiplealignment/MultipleAlignmentIO.java b/src/edu/mit/broad/prodinfo/multiplealignment/MultipleAlignmentIO.java
new file mode 100644
index 0000000..b22f95f
--- /dev/null
+++ b/src/edu/mit/broad/prodinfo/multiplealignment/MultipleAlignmentIO.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package edu.mit.broad.prodinfo.multiplealignment;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import edu.mit.broad.prodinfo.genomicplot.ParseException;
+
+public interface MultipleAlignmentIO {
+	MultipleAlignment load(String fileName) throws IOException, ParseException;
+	MultipleAlignment load(InputStream in) throws IOException, ParseException;
+	MultipleAlignment load(String fileName, List<String> sequencesToLoad) throws IOException, ParseException;
+	MultipleAlignment load(InputStream in, List<String> sequencesToLoad) throws IOException, ParseException;
+	void write(BufferedWriter bw, MultipleAlignment ma) throws IOException;
+	void write(BufferedWriter bw, MultipleAlignment ma, List<String> orderOfSequences) throws IOException ;
+	String getPreferredFileExtension();
+}
diff --git a/src/edu/mit/broad/prodinfo/multiplealignment/ReadTest.java b/src/edu/mit/broad/prodinfo/multiplealignment/ReadTest.java
new file mode 100644
index 0000000..b7c07c1
--- /dev/null
+++ b/src/edu/mit/broad/prodinfo/multiplealignment/ReadTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package edu.mit.broad.prodinfo.multiplealignment;
+
+import edu.mit.broad.prodinfo.multiplealignment.*;
+import edu.mit.broad.prodinfo.genomicplot.ParseException;
+import edu.mit.broad.prodinfo.multiplealignment.MultipleAlignment.AlignedSequence;
+import edu.mit.broad.prodinfo.sequence.Sequence;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+*
+* @author jrobinso
+*/
+public class ReadTest {
+    
+    public static void main(String [] args) throws IOException, ParseException {
+        
+        String in = "/Users/jrobinso/IGV/MAF/chr21.maf";
+        
+        //chr21:34,854,786-34,854,830
+        int start = 34854773;
+        int end = 34854841 + 1000;
+        
+        MAFIO mafio = new MAFIO();
+        
+        MAFAlignment maf = mafio.load(in, null, start, end) ;
+        
+        long t0 = System.currentTimeMillis();
+        List<AlignedSequence> sequences = maf.getAlignedSequences();
+        long dt = System.currentTimeMillis() - t0;
+        System.out.println("Elapsed time = " + dt);
+        
+        for(AlignedSequence sequence : sequences) {
+            Sequence seq = sequence.getSequence();
+            System.out.print(sequence.getName() + "\t");
+            System.out.println(seq.getSequenceBases());
+        }
+
+    }
+
+}
+
diff --git a/src/edu/mit/broad/prodinfo/sequence/Sequence.java b/src/edu/mit/broad/prodinfo/sequence/Sequence.java
new file mode 100644
index 0000000..99c0caa
--- /dev/null
+++ b/src/edu/mit/broad/prodinfo/sequence/Sequence.java
@@ -0,0 +1,547 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package edu.mit.broad.prodinfo.sequence;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import Jama.Matrix;
+
+import edu.mit.broad.prodinfo.chromosome.BasicGenomicAnnotation;
+import edu.mit.broad.prodinfo.genomicplot.GenomicAnnotation;
+
+public class Sequence {
+	private static final Pattern SOFT_MASKED_PAT = Pattern.compile("[acgt]+");
+	private static final Pattern SEQUENCE_GAP_PAT = Pattern.compile("[Nn]+");
+	private static final Pattern UNGAPPED_SEQUENCE_PAT = Pattern.compile("[^Nn]+");
+	
+	private String id;
+	private StringBuilder sequenceBases;
+	private short[] encodedSequence;
+	private Matrix vectorEncodedSequence;
+	private boolean forwardStrand = true;
+	private boolean encodeIgnoreCase = false;
+	
+	public static final char[] SHORT_READ_FLOW = {'T','A','C','G'};
+	public static final char[] LONG_READ_FLOW  = {'G','A','T','C'};
+	
+	public static final short SHORT_ENCODED_A = 0;
+	public static final short SHORT_ENCODED_C = 1;
+	public static final short SHORT_ENCODED_G = 2;
+	public static final short SHORT_ENCODED_T = 3;
+	public static final short SHORT_ENCODED_GAP = 4;
+	public static final short SHORT_ENCODED_a = 5;
+	public static final short SHORT_ENCODED_c = 6;
+	public static final short SHORT_ENCODED_g = 7;
+	public static final short SHORT_ENCODED_t = 8;
+	public static final short SHORT_ENCODED_N = 9;
+
+	public Sequence(String id) {
+		super();
+		this.id = id;
+		sequenceBases = new StringBuilder();
+	}
+	
+	public Sequence(String id, boolean isLarge) {
+		this(id);
+		if(isLarge) {
+			sequenceBases = new StringBuilder(850000000);
+		}
+	}
+
+	public String getId() {
+		return id;
+	}
+	
+	protected void setForwardStrand(boolean isForwardStrand) { this.forwardStrand = isForwardStrand;}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+	
+	public void unloadSequence() {
+		sequenceBases.delete(0, sequenceBases.length());
+		sequenceBases.trimToSize();
+	}
+	
+	public int getLength() {
+		return sequenceBases.length();
+	}
+	
+	public void setCharAt (int position, char newCharacter) throws IllegalAccessException {
+		if (sequenceBases == null) {
+			throw new IllegalAccessException("This methods is only implemented for un encoded sequences. sequenceBases is null, it must be non null.");
+		}
+		
+		sequenceBases.setCharAt(position, newCharacter);
+	}
+
+	public String getSequenceBases() {
+	    String bases = "";
+		if(sequenceBases.length() > 0) {
+			bases =  sequenceBases.toString();
+		} else if (encodedSequence != null && encodedSequence.length > 0) {
+			StringBuffer buf = new StringBuffer(encodedSequence.length);
+			if(!encodeIgnoreCase) {
+				for(int i = 0 ; i < encodedSequence.length; i++) {
+					switch (encodedSequence[i]) {
+					case SHORT_ENCODED_c : buf.append("c"); break;
+					case SHORT_ENCODED_C : buf.append("C"); break;
+					case SHORT_ENCODED_G : buf.append("G"); break;
+					case SHORT_ENCODED_g : buf.append("g"); break;
+					case SHORT_ENCODED_a : buf.append("a"); break;
+					case SHORT_ENCODED_A : buf.append("A"); break;
+					case SHORT_ENCODED_T : buf.append("T"); break;
+					case SHORT_ENCODED_t : buf.append("t"); break;
+					case SHORT_ENCODED_GAP : buf.append("-"); break;
+					default : buf.append("N"); break;
+					}
+				}
+
+			}else {
+				for(int i = 0 ; i < encodedSequence.length; i++) {
+					switch (encodedSequence[i]) {
+					case SHORT_ENCODED_A : buf.append("A"); break;
+					case SHORT_ENCODED_C : buf.append("C"); break;
+					case SHORT_ENCODED_G : buf.append("G"); break;
+					case SHORT_ENCODED_T : buf.append("T"); break;
+					case SHORT_ENCODED_GAP : buf.append("-"); break;
+					default : buf.append("N"); break;
+					}
+				}
+			}
+			bases = buf.toString();
+		} else if (vectorEncodedSequence != null) {
+			Random random = new Random();
+			int seqLength = vectorEncodedSequence.getColumnDimension();
+			StringBuffer buf = new StringBuffer(seqLength);
+			for(int j = 0; j < seqLength; j++) {
+				double draw = random.nextDouble();
+				int drawedBase = -1;
+				double cummulativeProbability = 0;
+				for(int i = 0; i < 4; i++) {
+					cummulativeProbability += vectorEncodedSequence.get(i,j);
+					if(draw <= cummulativeProbability ) {
+						drawedBase = i;
+						break;
+					}
+				}
+				
+				switch (drawedBase) {
+					case SHORT_ENCODED_A : buf.append("A"); break;
+					case SHORT_ENCODED_C : buf.append("C"); break;
+					case SHORT_ENCODED_G : buf.append("G"); break;
+					case SHORT_ENCODED_T : buf.append("T"); break;
+					default : buf.append("-"); break;				
+				}
+			}
+			bases = buf.toString();
+		}
+		
+		return bases;
+	}
+
+	public void setSequenceBases(String sequence) {
+		this.sequenceBases = new StringBuilder(sequence);
+	}
+	
+	public void appendToSequence(String partialSequence) {
+		sequenceBases.append(partialSequence);
+	}
+	
+	public void appendToSequence(char c) {
+		sequenceBases.append(c);
+	}
+	
+	public boolean isGap(int position) {
+		boolean isGap = false;
+		if(encodedSequence != null && encodedSequence.length >= position) {
+			isGap =  encodeIgnoreCase && (encodedSequence[position] == 4);
+		} else if (vectorEncodedSequence != null && vectorEncodedSequence.getColumnDimension() >= position) {
+			double maxProb = 0;
+			
+			for(int i = 0; i < 4; i++) {
+				double baseProbability = vectorEncodedSequence.get(i,position);
+				if(maxProb < baseProbability ) {
+					maxProb = baseProbability;
+				}
+			}
+			
+			isGap = maxProb == 0;
+		} else if (sequenceBases != null & sequenceBases.length() >= position) {
+			isGap = '-' == sequenceBases.charAt(position);
+		}
+		
+		return isGap;
+	}
+	
+	public List<GenomicAnnotation> getSoftmaskedRegions() {
+		List<GenomicAnnotation> softMaskedRegions = new ArrayList<GenomicAnnotation>();
+		if(sequenceBases == null) {
+			return softMaskedRegions;
+		}
+		
+		Matcher m = SOFT_MASKED_PAT.matcher(sequenceBases);
+		int i = 1;
+		while(m.find()) {
+			BasicGenomicAnnotation softMaskedReg = new BasicGenomicAnnotation(getId() + "_SoftmaskedReg_" + i++);
+			softMaskedReg.setStart(m.start() + 1);
+			softMaskedReg.setEnd(m.end() + 1);
+			softMaskedReg.setChromosome(getId());
+			softMaskedRegions.add(softMaskedReg);
+		}
+		return softMaskedRegions;
+	}
+	
+	public short[] getEncodedSequence () {
+		return encodedSequence;
+	}
+	
+	public Matrix getVectorEncodedSequence() {
+		return vectorEncodedSequence;
+	}
+	
+	public Matrix encodeSequenceAsVector() {
+		
+		vectorEncodedSequence = new Matrix(4, sequenceBases.length());
+		
+		for(int j = 0; j < sequenceBases.length(); j++) {
+			char c = sequenceBases.charAt(j);
+			if('a' == c || 'A' == c) {
+				vectorEncodedSequence.set(SHORT_ENCODED_A, j, 1);
+			} else if ('C' == c || 'c' == c) {
+				vectorEncodedSequence.set(SHORT_ENCODED_C, j, 1);
+			}else if ('G' == c || 'g' == c) {
+				vectorEncodedSequence.set(SHORT_ENCODED_G, j, 1);
+			}else if ('T' == c || 't' == c) {
+				vectorEncodedSequence.set(SHORT_ENCODED_T, j, 1);
+			} 
+		}	
+
+		return vectorEncodedSequence;
+	}
+	
+	public short[] encodeSequence() {
+		if(encodedSequence != null) {
+			return encodedSequence;
+		}
+		
+		short [] encodedSeq = new short[sequenceBases.length()];
+		
+		for(int i = 0; i < sequenceBases.length(); i++) {
+			char c = sequenceBases.charAt(i);
+			if('c' == c) {
+				encodedSeq[i] = SHORT_ENCODED_c;
+			}else if ('C' == c) {
+				encodedSeq[i] = SHORT_ENCODED_C;
+			}else if ('G' == c) {
+				encodedSeq[i] = SHORT_ENCODED_G;
+			}else if ('g' == c) {
+				encodedSeq[i] = SHORT_ENCODED_g;
+			}else if ('a' == c) {
+				encodedSeq[i] = SHORT_ENCODED_a;
+			}else if ('A' == c) {
+				encodedSeq[i] = SHORT_ENCODED_A;
+			}else if('t' == c) {
+				encodedSeq[i] = SHORT_ENCODED_t;
+			}else if('T' == c) {
+				encodedSeq[i] = SHORT_ENCODED_T;
+			}else {
+				encodedSeq[i] = SHORT_ENCODED_N;
+			}
+		}			
+
+		this.encodedSequence = encodedSeq;
+		encodeIgnoreCase = false;
+		return encodedSeq;
+	}
+	
+	public short[] encodeSequenceIgnoreCase() {
+		if(encodedSequence != null) {
+			return encodedSequence;
+		}
+		
+		short [] encodedSeq = new short[sequenceBases.length()];
+		
+		for(int i = 0; i < sequenceBases.length(); i++) {
+			char c = sequenceBases.charAt(i);
+			if('a' == c || 'A' == c) {
+				encodedSeq[i] = SHORT_ENCODED_A;
+			}else if ('C' == c || 'c' == c) {
+				encodedSeq[i] = SHORT_ENCODED_C;
+			}else if ('G' == c || 'g' == c) {
+				encodedSeq[i] = SHORT_ENCODED_G;
+			}else if ('T' == c || 't' == c) {
+				encodedSeq[i] = SHORT_ENCODED_T;
+			} else if ('-' ==  c) {
+				encodedSeq[i] = SHORT_ENCODED_GAP;
+			} else {
+				encodedSeq[i] = SHORT_ENCODED_GAP;  //All other letter codes are cosidered gaps.
+			}
+		}			
+
+		this.encodedSequence = encodedSeq;
+		encodeIgnoreCase = true;
+		return encodedSeq;
+	}
+	
+	
+	public List<Short> compute454Flow(char[] flowOrder) {
+		return compute454Flow(flowOrder, 1, sequenceBases.length());
+	}
+
+	public List<Short> compute454Flow(char[] flowOrder, int start, int end) {
+		char [] subSeqChrs = new char[end - start];
+		sequenceBases.getChars(start - 1, end - 1, subSeqChrs, 0);
+		ArrayList<Short> flow = new ArrayList<Short>();
+		int seqIdx = 0;
+		int flowIdx = 0;
+		char flowLetter = 0;
+		short flowRead = 0;
+		while(seqIdx < subSeqChrs.length) {
+			flowLetter = flowOrder[flowIdx % 4];
+			char seqChar = Character.toUpperCase(subSeqChrs[seqIdx]);
+			if(seqChar != 'A' && seqChar != 'T' && seqChar != 'G' && seqChar != 'C') {
+				System.err.println("Sequence Character " + seqChar + " is not a base, ignoring it");
+				seqIdx++;
+			}
+			if(flowLetter == seqChar) {
+				flowRead++;
+				seqIdx++;
+			} else {
+				flow.add(flowRead);
+				flowIdx++;
+				flowRead = 0;
+			}
+
+		}
+		// TODO Auto-generated method stub
+		return flow;
+	}
+	
+	public float gcContent() {
+		return computeGCContent(sequenceBases.toString());
+	}
+	
+	public boolean contains(String motif) {
+		Sequence revMotifSeq = new Sequence("rev_motif");
+		revMotifSeq.setSequenceBases(motif);
+		revMotifSeq.reverse();
+		String revMotif = revMotifSeq.getSequenceBases();
+		
+		return getSequenceBases().contains(motif) || getSequenceBases().contains(revMotif);
+	}
+	
+	public static float computeGCContent(String dnaString) {
+		int gcs = 0;
+		for(int i = 0; i < dnaString.length(); i++) {
+			char c = Character.toUpperCase(dnaString.charAt(i));
+			if('C' == c || 'G' == c) {
+				gcs++;
+			}
+		}
+		
+		return ((float)gcs)/((float)dnaString.length());
+	}
+	
+	public void reverse() {
+		String seqBases = getSequenceBases();
+		StringBuilder reversedSeq = new StringBuilder(seqBases.length());
+		for(int j = seqBases.length() - 1; j >= 0 ; j--) {
+			char c = seqBases.charAt(j);
+			if('c' == c) {
+				reversedSeq.append('g');
+			}else if ('C' == c) {
+				reversedSeq.append('G');
+			}else if ('G' == c) {
+				reversedSeq.append('C');
+			}else if ('g' == c) {
+				reversedSeq.append('c');
+			}else if ('a' == c) {
+				reversedSeq.append('t');
+			}else if ('A' == c) {
+				reversedSeq.append('T');
+			}else if('t' == c) {
+				reversedSeq.append('a');
+			}else if('T' == c) {
+				reversedSeq.append('A');
+			}else {
+				reversedSeq.append(c);
+			}
+		}
+		
+		sequenceBases = reversedSeq;
+		if(encodedSequence != null) {
+			encodedSequence = null;
+			encodeSequenceIgnoreCase();
+		} else if(vectorEncodedSequence != null) {
+			vectorEncodedSequence = null;
+			encodeSequenceAsVector();
+		}
+		forwardStrand = false;
+	}
+	
+	public static String reverseSequence(String seq) {
+		Sequence tmpSeq = new Sequence("tmp");
+		tmpSeq.setSequenceBases(seq);
+		tmpSeq.reverse();
+		return tmpSeq.getSequenceBases();
+	}
+	
+	public void uppercase() {
+		StringBuilder uppercasedSeq = new StringBuilder(sequenceBases.toString().toUpperCase());
+		sequenceBases = uppercasedSeq;
+	}
+	
+	/**
+	 * 
+	 * @param start - start of the region to extract the starting base will be included starting at 0
+	 * @param end - the end of the region to extract, the base at position end will not be included.
+	 * @return
+	 */
+	public SequenceRegion getRegion(int start, int end) {
+		SequenceRegion region = new SequenceRegion(getId());
+		region.setRegionStart(start);
+		region.setRegionEnd(end);
+		String bases = getSequenceBases();
+		if(bases != null && bases.length() > 0) {
+			region.setSequenceBases(bases.substring(start, end));
+		}
+		
+		return region;
+	}
+	
+	public void getRegion(SequenceRegion region) {
+		region.setSequenceBases(sequenceBases.substring(region.getStart(), region.getEnd()));
+	}
+	
+	public void getRegions(List<? extends SequenceRegion> regions) {
+		Iterator<? extends SequenceRegion> it = regions.iterator();
+		while(it.hasNext()) {
+			SequenceRegion reg = it.next();
+			getRegion(reg);
+		}
+		
+	}
+	
+	public SequenceRegion extractRegionBasedOnGC(float targetGC, int size, int buffer) {
+		SequenceRegion theRegion = null;
+		float closestGC = 0;
+
+		System.out.println("extracting region based on GC for sequence " + getId() + " of size " + getSequenceBases().length() + " buffer " + buffer);
+		
+		if(getSequenceBases() == null || getSequenceBases().length() < buffer) {
+			return theRegion;
+		}
+
+		for(int i = buffer; i < (getSequenceBases().length() - buffer - size); i += buffer) {
+			String currentSequence = getSequenceBases().substring(i - buffer, i + size + buffer - 1);
+			float currentGC = computeGCContent(currentSequence);
+			System.out.println("position " + i + " currentGC " + currentGC + ", targetGC " + targetGC + ",  closestGC distance to target " + Math.abs(closestGC - targetGC) + " currentGC distance to target " + Math.abs(targetGC - currentGC) );
+			if(Math.abs(closestGC - targetGC) > Math.abs(targetGC - currentGC) ){
+				closestGC = currentGC;
+				theRegion = new SequenceRegion(getId());
+				theRegion.setSequenceBases(currentSequence);
+				theRegion.setRegionStart(i - buffer);
+				theRegion.setEnd(i + size + buffer - 1);
+			}
+		}
+		System.out.println("Returning closest GC region: " + theRegion + " GC% " + closestGC);
+		return theRegion;
+	}
+
+	public boolean isForwardStrand() { return forwardStrand;}
+
+	public StringBuilder getSequenceBuilder() {
+		return sequenceBases == null || sequenceBases.length() == 0 
+			? new StringBuilder(getSequenceBases()) 
+			: sequenceBases;
+	}
+	
+	public int getGapsSize() {
+		int totalGaps = 0;
+		if(sequenceBases != null && sequenceBases.length() > 0) {
+			char [] baseArray = sequenceBases.toString().toUpperCase().toCharArray();
+			for(int i = 0; i < baseArray.length; i++) {
+				if(baseArray[i] == 'N') {
+					totalGaps++;
+				}
+			}
+		}
+		return totalGaps;
+	}
+	
+	public WindowSlider getSlider(int windowSize, int overlap) {
+		return WindowSlider.getSlider(this, windowSize, overlap);
+	}
+	
+	public List<SequenceRegion> chunk(int chunkSize, int chunkOverlap) {
+		int numOfChunks = (int) Math.floor(getLength()/((float)(chunkSize - chunkOverlap)));
+		int chunkStart = 0;
+		int chunkEnd   = 0;
+		System.out.println("\tChunking " + getId() + " number of chunks " + numOfChunks);
+		List<SequenceRegion> chunks = new ArrayList<SequenceRegion>(numOfChunks);
+		
+		for(int i = 0; i< numOfChunks; i++) {
+			chunkStart = i * (chunkSize - chunkOverlap);
+			chunkEnd   = chunkStart + chunkSize;
+			SequenceRegion chunk = getRegion(chunkStart, chunkEnd);
+			chunks.add(chunk);
+		}
+		if(chunkEnd < getLength()) {
+			chunkStart = chunkEnd - chunkOverlap;
+			SequenceRegion lastChunk = getRegion(chunkStart, getLength());
+			chunks.add(lastChunk);
+		}
+		
+		System.out.println("\tObtained " + chunks.size() + " chunks"); 
+		return chunks;
+	}
+
+	public int getEnd() {
+		return getLength();
+	}
+	
+	/**
+	 * Finds the list of ungapped regions in the sequence.
+	 * @return A list of two sized integer arrays with the start and end of the ungapped regions. The convention here is semi closed
+	 * 		   intervals, each list items is of the form [start, end).
+	 */
+	public List<int []> findUngappedSequenceChunks() {
+		Matcher m = UNGAPPED_SEQUENCE_PAT.matcher(getSequenceBases());
+		ArrayList<int []> gapUngapp = new ArrayList<int []>();
+		//System.out.println("Sequence for " + getId() + ": " + getSequenceBases());
+		while(m.find()) {
+			int [] ungappedReg = {m.start(), m.end()};
+			//System.out.println("\treg["+m.start()+"-"+m.end()+"]: " + getSequenceBases().substring(m.start(), m.end()));
+			gapUngapp.add(ungappedReg);
+		}
+		//System.out.println("");
+
+		return gapUngapp;			
+	}
+
+
+}
+
+
diff --git a/src/edu/mit/broad/prodinfo/sequence/SequenceRegion.java b/src/edu/mit/broad/prodinfo/sequence/SequenceRegion.java
new file mode 100644
index 0000000..f531426
--- /dev/null
+++ b/src/edu/mit/broad/prodinfo/sequence/SequenceRegion.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package edu.mit.broad.prodinfo.sequence;
+
+import java.util.List;
+
+import edu.mit.broad.prodinfo.chromosome.BasicGenomicAnnotation;
+import edu.mit.broad.prodinfo.genomicplot.GenomicAnnotation;
+import edu.mit.broad.prodinfo.genomicplot.TwoSubjectAnnotation;
+
+public class SequenceRegion extends Sequence implements GenomicAnnotation {
+	BasicGenomicAnnotation annotation;
+	private String containingSequenceId;
+	public static final int INF = 1000000000;
+	
+	public SequenceRegion(String containingSequenceId) {
+		super(null);
+		this.containingSequenceId = containingSequenceId;
+		annotation = new BasicGenomicAnnotation(containingSequenceId);
+		annotation.setSequence(this);
+		annotation.setEnd(INF);
+	}
+	public int getRegionEnd() {
+		return annotation.getEnd() == INF && super.getSequenceBases() != null && getSequenceBases().length() > 0 
+			? getSequenceBases().length() + annotation.getStart() - 1
+			: annotation.getEnd();
+	}
+	public void setRegionEnd(int regionEnd) {
+		annotation.setEnd(regionEnd);
+	}
+	public int getRegionStart() {
+		return annotation.getStart();
+	}
+	public void setRegionStart(int regionStart) {
+		annotation.setStart(regionStart);
+	}
+	
+	public WindowSlider getSlider(int windowSize, int overlap) {
+		return WindowSlider.getSlider(this, windowSize, overlap);
+	}
+	
+	public int getLength() {
+		if(getEnd() == INF && getSequenceBases() == null && getSequenceBases().length() == 0) {
+			return INF;
+		} else if(getEnd() < INF) {
+			return getEnd() - getStart();
+		} else {
+			return super.getLength();
+		}
+	}
+	
+	public String getOrientation() { return inReversedOrientation() ? "-" : "+"; }
+	public void setReversedOrientation(boolean reversed) { annotation.setOrientation(!reversed); }
+	
+	public String getContainingSequenceId() {
+		return containingSequenceId;
+	}
+	
+	public String getId() {
+		return super.getId() == null ? containingSequenceId + "_" + getStart() + "-" + getEnd() : super.getId();
+	}
+	
+	public int getStart() {
+		return annotation.getStart();
+	}
+	
+	public int getEnd() {
+		return annotation.getEnd();
+	}
+	
+	public double getScore() {
+		return annotation.getScore();
+	}
+	
+	public void setScore(double score) {
+		annotation.setScore(score);
+	}
+	
+	
+	public String getName() {
+		return getId();
+	}
+	
+	public void setId(String id) {
+		super.setId(id);
+		annotation.setName(id);
+	}
+	
+	public void setName(String name) {
+		setId(name);
+	}
+	
+	public boolean inReversedOrientation() {
+		return annotation.inReversedOrientation();
+	}
+	
+	public long getMiddle() {
+		return annotation.getMiddle();
+	}
+	
+	public void setStart(int start) {
+		setRegionStart(start);
+	}
+	
+	public void setEnd(int end) {
+		setRegionEnd(end);
+	}
+	public Sequence getSequence() {
+		return this;
+	}
+	public void setSequence(Sequence seq) {
+		super.setSequenceBases(seq.getSequenceBases());
+	}
+	
+	public String getChromosome() {
+		return annotation.getChromosome();
+	}
+	
+	public void setChromosome(String chr) {
+		annotation.setChromosome(chr);
+	}
+	public boolean overlaps(GenomicAnnotation other, int buffer) {
+		return annotation.overlaps(other, buffer);
+	}
+	public boolean overlaps(GenomicAnnotation other) {
+		return annotation.overlaps(other);
+	}
+	
+	public List<GenomicAnnotation> minus(GenomicAnnotation other) {
+		return annotation.minus(other);
+	}
+	
+	public List<GenomicAnnotation> minus(List<? extends GenomicAnnotation> others) {
+		return annotation.minus(others);
+	}
+	
+	public void takeIntersection(GenomicAnnotation other) {
+		annotation.takeIntersection(other);
+	}
+	
+	public void takeUnion(GenomicAnnotation other) {
+		annotation.takeUnion(other);
+	}
+	public void stitchTo(GenomicAnnotation other) {
+		annotation.stitchTo(other);
+	}
+	
+	public boolean isFlankedBy(TwoSubjectAnnotation twoSubjectAnnotation, int buffer) {
+		return annotation.isFlankedBy(twoSubjectAnnotation, buffer);
+	}
+	public int getFivePrimeBases() {
+		return annotation.getFivePrimeBases();
+	}
+	public int getThreePrimeBases() {
+		return annotation.getThreePrimeBases();
+	}
+	
+	public String toString() {
+		return getContainingSequenceId() + ":" + getRegionStart() + "-" + getRegionEnd();
+	}
+	
+	public String getLocationString() { return annotation.getLocationString();}
+	public String getChromosomeString() { return annotation.getChromosomeString(); }
+	
+	public SequenceRegion extractRegionBasedOnGC(float targetGC, int size, int buffer) {
+		SequenceRegion theRegion = super.extractRegionBasedOnGC(targetGC, size, buffer);
+		if(theRegion != null) {
+			theRegion.setRegionStart(getRegionStart() + theRegion.getRegionStart());
+			theRegion.setRegionEnd(getRegionStart() + theRegion.getRegionEnd());
+			theRegion.setChromosome(annotation.getChromosome());
+		}
+		return theRegion;
+	}
+	public boolean contains(GenomicAnnotation other) {
+		return annotation.contains(other);
+	}
+	public int getDistanceTo(GenomicAnnotation other) {
+		return annotation.getDistanceTo(other);
+	}
+	public void setOrientation(String orientation) {
+		annotation.setOrientation(orientation);
+		
+	}
+	public int compareTo(GenomicAnnotation arg0) {
+		return annotation.compareTo(arg0);
+	}
+	public List<GenomicAnnotation> disect(GenomicAnnotation a) {
+		return annotation.disect(a);
+	}
+	public List<GenomicAnnotation> disect(List<? extends GenomicAnnotation> disectors) {
+		return annotation.disect(disectors);
+	}
+	
+	public SequenceRegion getRegion(int start, int end) {
+		SequenceRegion region = super.getRegion(start, end);
+		if(annotation.getChromosome() != null) {
+			region.setChromosome(annotation.getChromosome());
+		}
+		return region;
+	}
+	public int getOrientedEnd() {
+		return annotation.getOrientedEnd();
+	}
+	public int getOrientedStart() {
+		return annotation.getOrientedStart();
+	}
+	public void addBlock(String name, int start, int end) {
+		//DO nothing as there is nothing to do here.
+		
+	}
+	public List<? extends GenomicAnnotation> getBlocks() {
+		return null;
+	}
+	public boolean mayHaveBlocks() {
+		return false;
+	}
+	
+}
diff --git a/src/edu/mit/broad/prodinfo/sequence/WindowSlider.java b/src/edu/mit/broad/prodinfo/sequence/WindowSlider.java
new file mode 100644
index 0000000..a9a501e
--- /dev/null
+++ b/src/edu/mit/broad/prodinfo/sequence/WindowSlider.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package edu.mit.broad.prodinfo.sequence;
+
+import java.util.Iterator;
+
+public class WindowSlider implements Iterator<SequenceRegion> {
+	
+	private int windowSize;
+	private int overlap;
+	private int atPosition = 0; 
+	private int seqStart;
+	private Sequence seq;
+
+	public static WindowSlider getSlider(Sequence seq, int windowSize, int overlap) {
+		WindowSlider ws = new WindowSlider(windowSize, overlap);
+		ws.seq = seq;
+		ws.seqStart = 0;
+		return ws;
+	}
+	
+	public static WindowSlider getSlider(SequenceRegion seq, int windowSize, int overlap) {
+		//System.out.println("Overloaded method used");
+		WindowSlider ws = new WindowSlider(windowSize, overlap);
+		ws.seq = seq;
+		ws.atPosition = seq.getStart();
+		ws.seqStart = seq.getStart();
+		return ws;
+	}
+	
+	WindowSlider(int windowSize, int overlap) {
+		this.windowSize = windowSize;
+		this.overlap    = overlap;
+	}
+	
+	public boolean hasNext() {
+		//System.out.println("atPosition " + atPosition  + " seq_end " + seq.getEnd());
+		return atPosition < seq.getEnd() - windowSize;
+	}
+
+	public SequenceRegion next() {
+		SequenceRegion window = seq.getRegion(atPosition - seqStart, atPosition - seqStart + windowSize);
+		window.setStart(atPosition);
+		window.setEnd(atPosition + windowSize);
+		atPosition = atPosition + windowSize - overlap;
+		return window;
+	}
+
+	public void remove() {
+		// DO NOTHING
+
+	}
+
+}
diff --git a/src/hdf5/TestH5String.java b/src/hdf5/TestH5String.java
deleted file mode 100644
index 10d2d44..0000000
--- a/src/hdf5/TestH5String.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-package hdf5;
-
-import ncsa.hdf.hdf5lib.H5;
-import ncsa.hdf.hdf5lib.HDF5Constants;
-
-/**
- * Test of reading HDF5 string.
- */
-public class TestH5String {
-    public static void main(String[] argv) {
-        int fid = -1, did = -1, tid = -1, sid = -1;
-        long[] dims = {2};
-        int rank = 1;
-        int strLength = 14;
-        String[] strIn = {"test string 1 ", "test string 2 "};
-        String[] strOut = null;
-        byte[] byteBuff = null;
-
-        try {
-            // create a new file
-            fid = H5.H5Fcreate("testH5string.h5",
-                    HDF5Constants.H5F_ACC_TRUNC,
-                    HDF5Constants.H5P_DEFAULT,
-                    HDF5Constants.H5P_DEFAULT);
-
-            // create a new dataset of two strings
-            tid = H5.H5Tcopy(HDF5Constants.H5T_C_S1);
-            H5.H5Tset_size(tid, strLength);
-            sid = H5.H5Screate_simple(1, dims, null);
-            did = H5.H5Dcreate(fid, "/string", tid, sid, HDF5Constants.H5P_DEFAULT);
-            byteBuff = (strIn[0] + strIn[1]).getBytes(); // for large number of strings, use StringBuffer
-            H5.H5Dwrite(did, tid, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, HDF5Constants.H5P_DEFAULT, byteBuff);
-
-            try {
-                H5.H5Tclose(tid);
-            } catch (Exception ex) {
-            }
-            try {
-                H5.H5Sclose(sid);
-            } catch (Exception ex) {
-            }
-            try {
-                H5.H5Dclose(did);
-            } catch (Exception ex) {
-            }
-
-
-            // read the string from file and print it out.
-            did = H5.H5Dopen(fid, "/string");
-            tid = H5.H5Dget_type(did);
-            sid = H5.H5Dget_space(did);
-
-            int numberOfStrings = 1;
-            for (int i = 0; i < rank; i++)
-                numberOfStrings *= (int) dims[i];
-
-            // figure out the string size and number of strings
-            int stringLength = (int) H5.H5Tget_size(tid);
-            int bufferSize = numberOfStrings * stringLength;
-            byteBuff = new byte[bufferSize];
-
-            // read the string data into byte buff
-            int mspace = HDF5Constants.H5S_ALL;
-            int fspace = HDF5Constants.H5S_ALL;
-            int plist = HDF5Constants.H5P_DEFAULT;
-            H5.H5Dread(did, tid, mspace, fspace, plist, byteBuff);
-
-            // convert byte array into string array
-            strOut = new String[numberOfStrings];
-            for (int i = 0; i < numberOfStrings; i++) {
-                strOut[i] = new String(byteBuff, i * stringLength, stringLength).trim();
-            }
-        } catch (Exception ex) {
-            System.out.println(ex);
-        } finally {
-            try {
-                H5.H5Tclose(tid);
-            } catch (Exception ex) {
-            }
-            try {
-                H5.H5Sclose(sid);
-            } catch (Exception ex) {
-            }
-            try {
-                H5.H5Dclose(did);
-            } catch (Exception ex) {
-            }
-            try {
-                H5.H5Fclose(fid);
-            } catch (Exception ex) {
-            }
-        }
-
-        //print out string array
-        if (strOut != null) {
-            for (int i = 0; i < strOut.length; i++)
-                System.out.println(strOut[i]);
-        }
-    }
-}
-
diff --git a/src/log4j.properties b/src/log4j.properties
index 8dd7395..5779623 100644
--- a/src/log4j.properties
+++ b/src/log4j.properties
@@ -1,6 +1,6 @@
-#log4j.rootLogger=INFO, stdout, R
-log4j.rootLogger=INFO, stdout
-#log4j.logger.org.broad.igv=DEBUG, stdout, R
+log4j.rootLogger=INFO, stdout, R
+#log4j.rootLogger=DEBUG, stdout, R
+#log4j.logger.org.broad.igv.sam=DEBUG, stdout, R
 
 
 log4j.appender.stdout=org.apache.log4j.ConsoleAppender
diff --git a/src/org/broad/igv/Globals.java b/src/org/broad/igv/Globals.java
new file mode 100644
index 0000000..cb6086a
--- /dev/null
+++ b/src/org/broad/igv/Globals.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv;
+
+import org.apache.log4j.Logger;
+
+import java.awt.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.regex.Pattern;
+
+/**
+ * User: jrobinso
+ * Date: Feb 3, 2010
+ */
+public class Globals {
+
+    private static Logger logger = Logger.getLogger(Globals.class);
+
+
+    public static Color VERY_LIGHT_GREY = new Color(230, 230, 230);
+
+    /**
+     * CONSTANTS
+     */
+    final public static String CHR_ALL = "All";
+    public static boolean headless = false;
+    public static boolean suppress = false;
+    public static boolean batch = false;
+    /**
+     * Field description
+     */
+    final public static String SESSION_FILE_EXTENSION = ".xml";
+    /**
+     * GENOME ARCHIVE CONSTANTS
+     */
+    final public static String GENOME_FILE_EXTENSION = ".genome";
+    final public static String SERVER_GENOME_LIST_HEADER = "<Server-Side Genome List>";
+    final public static String ZIP_EXTENSION = ".zip";
+    final public static String FASTA_GZIP_FILE_EXTENSION = ".gz";
+    final public static String GENOME_ARCHIVE_PROPERTY_FILE_NAME = "property.txt";
+    final public static String GENOME_ARCHIVE_ID_KEY = "id";
+    final public static String GENOME_ARCHIVE_NAME_KEY = "name";
+    final public static String GENOME_ARCHIVE_VERSION_KEY = "version";
+    final public static String GENOME_ORDERED_KEY = "ordered";
+    final public static String GENOME_GENETRACK_NAME = "geneTrackName";
+    final public static String GENOME_ARCHIVE_CYTOBAND_FILE_KEY = "cytobandFile";
+    final public static String GENOME_ARCHIVE_GENE_FILE_KEY = "geneFile";
+    final public static String GENOME_ARCHIVE_SEQUENCE_FILE_LOCATION_KEY = "sequenceLocation";
+    final public static String GENOME_SEQUENCE_FOLDER = "sequence";
+    final public static String GENOME_CACHE_FOLDER_NAME = "genomes";
+    final static public Pattern commaPattern = Pattern.compile(",");
+    final static public Pattern tabPattern = Pattern.compile("\t");
+    final static public Pattern colonPattern = Pattern.compile(":");
+    final static public Pattern dashPattern = Pattern.compile("-");
+    final static public Pattern equalPattern = Pattern.compile("=");
+    public static List emptyList = new ArrayList();
+    public static String VERSION;
+    public static String BUILD;
+    public static String TIMESTAMP;
+    public static final String NO_FEATURES_FOUND_WARNING = "No features were found in this file with chromosomes mapped to the current genome";
+
+    static {
+        Properties properties = new Properties();
+        try {
+            properties.load(Globals.class.getResourceAsStream("/resources/about.properties"));
+        }
+        catch (IOException e) {
+            logger.error("*** Error retrieving version and build information! ***", e);
+        }
+        Globals.VERSION = properties.getProperty("version", "???");
+        Globals.BUILD = properties.getProperty("build", "???");
+        Globals.TIMESTAMP = properties.getProperty("timestamp", "???");
+    }
+
+    public static void setHeadless(boolean bool) {
+        headless = bool;
+    }
+
+    public static boolean isHeadless() {
+        return headless;
+    }
+
+    public static void setSuppress(boolean bool) {
+        suppress = bool;
+    }
+
+    public static boolean isSuppress() {
+        return suppress;
+    }
+
+    public static String applicationString() {
+        return "IGV Version " + VERSION + " (" + BUILD + ")" + TIMESTAMP;
+    }
+
+    public static String versionString() {
+        return "<html>Version " + VERSION + " (" + BUILD + ")<br>" + TIMESTAMP;
+    }
+}
diff --git a/src/org/broad/igv/IGVConstants.java b/src/org/broad/igv/IGVConstants.java
deleted file mode 100644
index a1d5fcb..0000000
--- a/src/org/broad/igv/IGVConstants.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv;
-
-//~--- JDK imports ------------------------------------------------------------
-
-import org.broad.igv.track.TrackType;
-
-import javax.swing.filechooser.FileSystemView;
-import java.awt.*;
-import java.io.File;
-import java.util.Properties;
-import java.util.regex.Pattern;
-
-/**
- * @author jrobinso
- */
-public class IGVConstants {
-
-    /**
-     * CONSTANTS
-     */
-    final public static String CHR_ALL = "All";
-    /**
-     * Field description
-     */
-    final public static int groupGap = 10;
-    /**
-     * Field description
-     */
-    final public static String APPLICATION_NAME = "IGV";
-    /**
-     * Field description
-     */
-    final public static String APPLICATION_LONG_NAME = "Integrative Genomics Viewer";
-    /**
-     * Field description
-     */
-    final public static boolean IS_WINDOWS =
-            System.getProperty("os.name").toLowerCase().startsWith("windows");
-    /**
-     * Field description
-     */
-    final public static boolean IS_MAC =
-            System.getProperty("os.name").toLowerCase().startsWith("mac");
-    /**
-     * Field description
-     */
-    final public static boolean IS_LINUX =
-            System.getProperty("os.name").toLowerCase().startsWith("linux");
-    /**
-     * Field description
-     */
-    final public static Dimension preferredSize = new Dimension(1000, 750);
-    // To support mutation track overlay.  Generalize later.  Cancer specific option.
-    public static TrackType overlayTrackType = TrackType.MUTATION;
-    // Default user folder
-    /**
-     * Field description
-     */
-    private static String DEFAULT_USER_DIRECTORY;
-    final public static File IGV_TEMP_DIRECTORY;
-    final public static File GENOME_CACHE_DIRECTORY;
-    public static final String DATA_SERVER_REGISTRY = "http://www.broadinstitute.org/igvdata/$$_dataServerRegistry.txt";
-    final public static String DEFAULT_IGV_DIRECTORY;
-
-
-    static {
-
-        StringBuffer path = new StringBuffer();
-        Properties properties = System.getProperties();
-        path.append(properties.getProperty("user.home"));
-        path.append(properties.getProperty("file.separator"));
-
-        if (IGVConstants.IS_MAC) {
-            path.append(".igv");
-        } else {
-            path.append("igv");
-        }
-        DEFAULT_IGV_DIRECTORY = path.toString().trim();
-
-        path = new StringBuffer();
-        path.append(System.getProperty("java.io.tmpdir"));
-        IGV_TEMP_DIRECTORY = new File(path.toString());
-        if (!IGV_TEMP_DIRECTORY.exists()) {
-            IGV_TEMP_DIRECTORY.mkdir();
-        }
-
-        path = new StringBuffer();
-        properties = System.getProperties();
-
-        if (new File(DEFAULT_IGV_DIRECTORY).exists()) {
-            path.append(DEFAULT_IGV_DIRECTORY);
-            path.append(properties.getProperty("file.separator"));
-        }
-
-        path.append(IGVConstants.GENOME_CACHE_FOLDER_NAME);
-        GENOME_CACHE_DIRECTORY = new File(path.toString());
-
-    }
-
-    public static synchronized String getDefaultUserDirectory() {
-        if (DEFAULT_USER_DIRECTORY == null) {
-
-            if (IGVConstants.IS_WINDOWS) {
-                DEFAULT_USER_DIRECTORY =
-                        FileSystemView.getFileSystemView().getDefaultDirectory().getAbsolutePath();
-            } else {
-                DEFAULT_USER_DIRECTORY =
-                        FileSystemView.getFileSystemView().getDefaultDirectory().getAbsolutePath();
-            }
-        }
-        return DEFAULT_USER_DIRECTORY;
-    }
-
-    // Default user folder
-    /**
-     * Field description
-     */
-
-    private static boolean headless = false;
-
-    public static void setHeadless(boolean bool) {
-        headless = bool;
-    }
-
-    public static boolean isHeadless() {
-        return headless;
-    }
-
-
-    /**
-     * Field description
-     */
-    final public static String SESSION_FILE_EXTENSION = ".xml";
-    /**
-     * Field description
-     */
-    final public static String OVERWRITE_SESSION_MESSAGE =
-            "<html>Opening a session will unload all current data. " + "<br>Are you sure you wish to continue?";
-    /**
-     * Field description
-     */
-    final public static String NEW_SESSION_MESSAGE =
-            "<html>Creating a new session will unload all current data. " + "<br>Are you sure you wish to continue?";
-    /**
-     * GENOME ARCHIVE CONSTANTS
-     */
-    final public static String GENOME_FILE_EXTENSION = ".genome";
-    final public static String SERVER_GENOME_LIST_HEADER = "<Server-Side Genome List>";
-    final public static String ZIP_EXTENSION = ".zip";
-    final public static String FASTA_GZIP_FILE_EXTENSION = ".gz";
-    final public static String GENOME_ARCHIVE_PROPERTY_FILE_NAME = "property.txt";
-    final public static String GENOME_ARCHIVE_ID_KEY = "id";
-    final public static String GENOME_ARCHIVE_NAME_KEY = "name";
-    final public static String GENOME_ARCHIVE_VERSION_KEY = "version";
-    final public static String GENOME_ORDERED_KEY = "ordered";
-    final public static String GENOME_GENETRACK_NAME = "geneTrackName";
-    final public static String GENOME_ARCHIVE_CYTOBAND_FILE_KEY = "cytobandFile";
-    final public static String GENOME_ARCHIVE_GENE_FILE_KEY = "geneFile";
-    final public static String GENOME_ARCHIVE_SEQUENCE_FILE_LOCATION_KEY = "sequenceLocation";
-    final public static String GENOME_SEQUENCE_FOLDER = "sequence";
-    final public static String GENOME_CACHE_FOLDER_NAME = "genomes";
-    final public static String DEFAULT_SERVER_GENOME_ARCHIVE_LIST =
-            "http://igv.broadinstitute.org/genomes/genomes.txt";
-    final public static String CANNOT_ACCESS_SERVER_GENOME_LIST =
-            "The Genome server is currently inaccessible.";
-    final public static String INVALID_SERVER_GENOME_LIST_HEADER =
-            "Genomes cannot be retrieved from the server. " + "The server-side genome list is invalid!";
-    // Session Folder
-    /**
-     * Field description
-     */
-    final public static String SESSION_FOLDER = "/igv_sessions";
-    /**
-     * Field description
-     */
-    final public static int NUMBER_OF_RECENT_SESSIONS_TO_LIST = 3;
-    /**
-     * Field description
-     */
-    final public static String DEFAULT_SESSION_FILE = "igv_session" + SESSION_FILE_EXTENSION;
-    // URLs
-    /**
-     * Field description
-     */
-    final static public String SERVER_BASE_URL = "http://www.broadinstitute.org/";
-    /**
-     * Field description
-     */
-    final public static String IGV_LOG_SERVER_URL = SERVER_BASE_URL + "igv/LogServlet";
-    // Colors
-    // float[] hsb = Color.RGBtoHSB(255, 255, 210, null);
-    /**
-     * Field description
-     */
-    final static public Color LIGHT_YELLOW = new Color(255, 244, 201);
-    /**
-     * Field description
-     */
-    final public static Color VERY_LIGHT_GRAY = new Color(238, 239, 240);
-    /**
-     * Field description
-     */
-    public static Color NO_DATA_COLOR = new Color(200, 200, 200, 150);
-    // GENOME
-    /**
-     * Field description
-     */
-    final static public String IMPORT_GENOME_LIST_MENU_ITEM = "Import Genome...";
-    /**
-     * Field description
-     */
-    final static public String LOAD_GENOME_LIST_MENU_ITEM = "Load Genome...";
-    /**
-     * Field description
-     */
-    final static public String REMOVE_GENOME_LIST_MENU_ITEM = "Remove Imported Genomes...";
-    /**
-     * Field description
-     */
-    final static public String GENOME_LIST_SEPARATOR = "--SEPARATOR--";
-    final static public Pattern commaPattern = Pattern.compile(",");
-    final static public Pattern tabPattern = Pattern.compile("\t");
-    final static public Pattern colonPattern = Pattern.compile(":");
-    final static public Pattern dashPattern = Pattern.compile("-");
-    final static public Pattern equalPattern = Pattern.compile("=");
-}
diff --git a/src/org/broad/igv/IGVPreferences.java b/src/org/broad/igv/IGVPreferences.java
index a1ccdc1..3cc7550 100644
--- a/src/org/broad/igv/IGVPreferences.java
+++ b/src/org/broad/igv/IGVPreferences.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,6 +23,7 @@
 package org.broad.igv;
 
 import org.apache.log4j.Logger;
+import org.broad.igv.ui.UIConstants;
 
 import java.io.*;
 import java.util.Hashtable;
@@ -38,12 +39,9 @@ import java.util.Map;
 public class IGVPreferences {
 
     private static Logger log = Logger.getLogger(IGVPreferences.class);
-    static File rootDir = new File(IGVConstants.DEFAULT_IGV_DIRECTORY);
-    static File prefFile = new File(rootDir, "prefs.properties");
     static Hashtable<String, String> cache = null;
     boolean suspendStorage = false;
 
-
     public void put(String key, String value) {
         if (cache == null) {
             loadPreferences();
@@ -92,6 +90,11 @@ public class IGVPreferences {
 
     private synchronized void loadPreferences() {
         cache = new Hashtable();
+        File rootDir = UIConstants.getIgvDirectory();
+        if (!rootDir.exists()) {
+            rootDir.mkdir();
+        }
+        File prefFile = new File(rootDir, "prefs.properties");
         if (prefFile.exists()) {
             BufferedReader reader = null;
             try {
@@ -130,10 +133,11 @@ public class IGVPreferences {
         if (cache != null) {
             PrintWriter pw = null;
             try {
+                File rootDir = UIConstants.getIgvDirectory();
                 if (!rootDir.exists()) {
                     rootDir.mkdir();
                 }
-
+                File prefFile = new File(rootDir, "prefs.properties");
                 pw = new PrintWriter(new BufferedWriter(new FileWriter(prefFile)));
                 for (Map.Entry<String, String> entry : cache.entrySet()) {
                     pw.print(entry.getKey());
diff --git a/src/org/broad/igv/PreferenceManager.java b/src/org/broad/igv/PreferenceManager.java
index 7f0532c..5f04568 100644
--- a/src/org/broad/igv/PreferenceManager.java
+++ b/src/org/broad/igv/PreferenceManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -24,20 +24,21 @@ package org.broad.igv;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
-import org.broad.igv.feature.Mutation;
 import org.broad.igv.maf.MAFManager;
 import org.broad.igv.renderer.ColorScaleFactory;
 import org.broad.igv.renderer.ContinuousColorScale;
 import org.broad.igv.track.TrackType;
 import org.broad.igv.ui.AboutDialog;
+import org.broad.igv.ui.UIConstants;
 import org.broad.igv.ui.util.PropertyManager;
+import org.broad.igv.ui.util.ColorTable;
+import org.broad.igv.util.IGVHttpUtils;
+
 import static org.broad.igv.ui.util.UIUtilities.getcommaSeparatedRGBString;
 
 import java.awt.*;
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.util.*;
 import java.util.List;
 import java.util.prefs.BackingStoreException;
@@ -72,23 +73,17 @@ public class PreferenceManager implements PropertyManager {
     public static final String TRACK_PROPERTIES_URL_KEY = "TRACK_PROPERTIES_URL";
     public static final String COLOR_SCALE_KEY = "COLOR_SCALE_";
     final public static String FRAME_BOUNDS_KEY = "IGVMainFrame.Bounds";
-    final public static String DISPLAYABLE_ATTRIBUTES_KEY = "IGVMainFrame.Attributes.Displayable";
     final public static String DRAW_EXON_NUMBERS_KEY = "DRAW_EXON_NUMBERS";
-    final public static String SESSION_NAME_KEY = "IGVMainFrame.Session.Name";
-    final public static String SESSION_GENOME_KEY = "IGVMainFrame.Session.Genome";
-    final public static String SESSION_TRACK_FILES_KEY = "IGVMainFrame.Session.Track.Files";
-    final public static String SESSION_ATTRIBUTE_FILE_KEY = "IGVMainFrame.Session.Attribute.File";
     final public static String RECENT_SESSION_KEY = "IGVMainFrame.Session.recent.sessions";
     final public static String TRACK_HEIGHT_KEY = "IGVMainFrame.track.height";
     final public static String CHART_TRACK_HEIGHT_KEY = "IGVMainFrame.chart.track.height";
     final public static String SHOW_MISSING_DATA_KEY = "IGVMainFrame.track.show.missing.data";
     final public static String SHOW_ATTRIBUTE_VIEWS_KEY = "IGVMainFrame.track.show.attribute.views";
-    final public static String SHOW_ROI_KEY = "IGVMainFrame.track.show.regionsOfInterest";
     final public static String SHOW_SINGLE_TRACK_PANE_KEY = "IGVMainFrame.single.track.pane";
-    final public static String FEATURE_FISHBONE_STYLE = "IGVMainFrame.feature.fishbone.style";
-    final public static String GENOMES_SEQUENCE_URL = "IGVMainFrame.genome.sequence.dir";
+
     final public static String JOIN_ADJACENT_SEGMENTS_KEY = "IGVMainFrame.join.adjacent.segments";
     final public static String SHOW_REGION_TOOL_KEY = "IGVMainFrame.show.region.tool";
+    final public static String SHOW_REGION_BARS = "SHOW_REGION_BARS";
     final public static String LAST_EXPORTED_REGION_DIRECTORY = "LAST_EXPORTED_REGION_DIRECTORY";
     final static public String LAST_TRACK_DIRECTORY = "LAST_TRACK_DIRECTORY";
     final static public String LAST_ATTRIBUTE_FILE_DIRECTORY = "LAST_ATTRIBUTE_FILE_DIRECTORY";
@@ -104,7 +99,6 @@ public class PreferenceManager implements PropertyManager {
     final public static String MUTATION_SYNONYMOUS_COLOR_KEY = "MUTATION_SYNONYMOUS_COLOR_KEY";
     final public static String MUTATION_TARGETED_REGION_COLOR_KEY = "MUTATION_TARGETED_REGION_COLOR_KEY";
     final public static String MUTATION_UNKNOWN_COLOR_KEY = "MUTATION_UNKNOWN_COLOR_KEY";
-    final public static String ENABLE_IMAGE_CACHING_KEY = "ENABLE_IMAGE_CACHING_KEY";
     final public static String DIRECT_DRAW_DISABLED_KEY = "DIRECT_DRAW_DISABLED_KEY";
     final public static String OVERLAY_TRACKS_KEY = "OVERLAY_TRACKS_KEY";
     final public static String DISPLAY_OVERLAY_TRACKS_KEY = "DISPLAY_OVERLAY_TRACKS_KEY";
@@ -112,7 +106,7 @@ public class PreferenceManager implements PropertyManager {
     final public static String COLOR_OVERLAY_KEY = "COVER_OVERLAY_KEY";
     final public static String ENABLE_LINKED_SORTING = "ENABLE_LINKED_SORTING";
     final public static String TRACK_ATTRIBUTE_NAME_KEY = "TRACK_ATTRIBUTE_NAME_KEY";
-    final public static String DATA_SERVER_URL_KEY = "MASTER_RESOURCE_FILE_KEY";
+
     final public static String CHECKED_RESOURCES_KEY = "CHECKED_RESOURCES_KEY";
     final public static String DEFINE_GENOME_INPUT_DIRECTORY_KEY = "DEFINE_GENOME_INPUT_DIRECTORY_KEY";
     final public static String LAST_CYTOBAND_DIRECTORY_KEY = "LAST_CYTOBAND_DIRECTORY_KEY";
@@ -122,9 +116,52 @@ public class PreferenceManager implements PropertyManager {
     final public static String MAF_SPECIES_KEY = "MAF_SPECIES_KEY";
     final public static String PROBE_MAPPING_KEY = "PROBE_MAPPING_KEY";
     final public static String SEARCH_ZOOM = "SEARCH_ZOOM";
+    final public static String NORMALIZE_COVERAGE = "NORMALIZE_COVERAGE";
+    public static final String SHOW_EXPAND_ICON = "SHOW_EXPAND_ICON";
+
+    public static final String SHOW_SIZE_WARNING = "SHOW_SIZE_WARNING";
+    public static final String SHOW_GENOME_SERVER_WARNING = "SHOW_GENOME_SERVER_WARNING";
+
+    final public static String USE_PROXY = "PROXY.USE";
+    final public static String PROXY_HOST = "PROXY.HOST";
+    final public static String PROXY_PORT = "PROXY.PORT";
+    final public static String PROXY_AUTHENTICATE = "PROXY.AUTHENTICATE";
+    final public static String PROXY_USER = "PROXY.USERNAME";
+    final public static String PROXY_PW = "PROXY.PW";
+
+    final public static String KNOWN_SNPS = "KNOWN_SNPS_FILE";
 
     final public static String USE_BYTE_RANGE = "UseHttpByteRange";
 
+    final public static String DATA_SERVER_URL_KEY = "MASTER_RESOURCE_FILE_KEY";
+    final public static String GENOMES_SEQUENCE_URL = "IGVMainFrame.genome.sequence.dir";
+
+    public static String DEFAULT_DATA_SERVER_URL;
+    public static String DEFAULT_GENOME_SERVER_URL;
+
+
+    static {
+        Properties properties = new Properties();
+        try {
+            properties.load(AboutDialog.class.getResourceAsStream("/resources/about.properties"));
+            DEFAULT_DATA_SERVER_URL = properties.getProperty("master-resource-url", "http://www.broadinstitute.org/igvdata/$$_dataServerRegistry.txt");
+            if (DEFAULT_DATA_SERVER_URL.equals("@DEFAULT_MASTER_RESOURCE_URL")) {
+                DEFAULT_DATA_SERVER_URL = "http://www.broadinstitute.org/igvdata/$$_dataServerRegistry.txt";
+            }
+
+            DEFAULT_GENOME_SERVER_URL = properties.getProperty("master-genome-url", "http://igv.broadinstitute.org/genomes/genomes.txt");
+            if (DEFAULT_GENOME_SERVER_URL.equals("@DEFAULT_MASTER_GENOME_URL")) {
+                DEFAULT_GENOME_SERVER_URL = "http://igv.broadinstitute.org/genomes/genomes.txt";
+            }
+
+            //GENOMES_SEQUENCE_URL
+        } catch (IOException e) {
+            DEFAULT_DATA_SERVER_URL = "http://www.broadinstitute.org/igvdata/$$_dataServerRegistry.txt";
+            DEFAULT_GENOME_SERVER_URL = "http://igv.broadinstitute.org/genomes/genomes.txt";
+        }
+
+    }
+
     /**
      * The preference cache
      */
@@ -143,6 +180,7 @@ public class PreferenceManager implements PropertyManager {
     private Map<String, Object> objectCache = new Hashtable();
     private Map<TrackType, ContinuousColorScale> colorScaleCache = new Hashtable();
 
+
     public static PreferenceManager getInstance() {
 
         return instance;
@@ -168,6 +206,7 @@ public class PreferenceManager implements PropertyManager {
                     preferences.put(key, value);
                 }
             }
+            oldPreferences.clear();
             preferences.resumeStorage();
             preferences.put("migrated", "true");
 
@@ -176,6 +215,15 @@ public class PreferenceManager implements PropertyManager {
         }
     }
 
+    public void mergePreferences(Map<String, String> newPrefs) {
+        for (Map.Entry<String, String> entry : newPrefs.entrySet()) {
+            String key = entry.getKey();
+            if (!newPrefs.containsKey(key)) {
+                put(key, entry.getValue());
+            }
+        }
+    }
+
 
     /**
      * Return the preference as a boolean value.
@@ -201,13 +249,21 @@ public class PreferenceManager implements PropertyManager {
         return boolValue.booleanValue();
     }
 
+
     public boolean isMapProbesToGenes() {
         return getBooleanPreference(PROBE_MAPPING_KEY, false);
     }
 
+    public boolean isShowRegionBars() {
+        return getBooleanPreference(SHOW_REGION_BARS, false);
+    }
+
+    public void setShowRegionBars(boolean show) {
+        put(SHOW_REGION_BARS, String.valueOf(show));
+    }
 
     public boolean isJoinAdjacentSegments() {
-        return getBooleanPreference(JOIN_ADJACENT_SEGMENTS_KEY, true);
+        return getBooleanPreference(JOIN_ADJACENT_SEGMENTS_KEY, false);
     }
 
     public boolean isUseByteRange() {
@@ -225,6 +281,7 @@ public class PreferenceManager implements PropertyManager {
         if (booleanCache.containsKey(key)) {
             booleanCache.put(key, new Boolean(value));
         }
+        objectCache.remove(key);
     }
 
     private void clearCaches() {
@@ -242,7 +299,7 @@ public class PreferenceManager implements PropertyManager {
 
     public void putAll(Map<String, String> updatedPrefs) {
         for (Map.Entry<String, String> entry : updatedPrefs.entrySet()) {
-            if (entry.getValue() == null) {
+            if (entry.getValue() == null || entry.getValue().trim().length() == 0) {
                 remove(entry.getKey());
 
             } else {
@@ -250,7 +307,6 @@ public class PreferenceManager implements PropertyManager {
             }
         }
         clearCaches();
-
     }
 
 
@@ -277,12 +333,20 @@ public class PreferenceManager implements PropertyManager {
         stringCache.clear();
         booleanCache.clear();
         objectCache.clear();
+
+        // Also clear the old preferences form the old JavaPreferences store
+        Preferences oldPreferences = Preferences.userNodeForPackage(PreferenceManager.class);
+        try {
+            oldPreferences.clear();
+        } catch (BackingStoreException e) {
+            log.error("Error clearing preferences store: ", e);
+        }
     }
 
 
     public String getGenomeListURL() {
         return get(PreferenceManager.GENOMES_SEQUENCE_URL,
-                IGVConstants.DEFAULT_SERVER_GENOME_ARCHIVE_LIST);
+                DEFAULT_GENOME_SERVER_URL);
     }
 
 
@@ -295,11 +359,6 @@ public class PreferenceManager implements PropertyManager {
         this.put(OVERLAY_TRACKS_KEY, String.valueOf(value));
     }
 
-
-    public boolean getDrawExonNumbers() {
-        return getBooleanPreference(DRAW_EXON_NUMBERS_KEY, false);
-    }
-
     public List<String> getMafSpecies() {
         String tmp = get(MAF_SPECIES_KEY, null);
 
@@ -326,10 +385,6 @@ public class PreferenceManager implements PropertyManager {
     }
 
 
-    public void setDrawExonNumbers(boolean value) {
-        this.put(DRAW_EXON_NUMBERS_KEY, String.valueOf(value));
-    }
-
     /**
      * @return
      */
@@ -344,24 +399,6 @@ public class PreferenceManager implements PropertyManager {
         return getBooleanPreference(DISPLAY_OVERLAY_TRACKS_KEY, true);
     }
 
-    /**
-     * @param value
-     */
-    public void setDisplayOverlayTracks(boolean value) {
-        this.remove(DISPLAY_OVERLAY_TRACKS_KEY);
-        this.put(DISPLAY_OVERLAY_TRACKS_KEY, String.valueOf(value));
-    }
-
-    /**
-     * @return
-     */
-    public boolean getDirectDrawDisabled() {
-
-        boolean systemDefault = (SYSTEM_DEFAULT_FOR_DIRECT_DRAW == null)
-                ? true : Boolean.getBoolean(SYSTEM_DEFAULT_FOR_DIRECT_DRAW);
-
-        return getBooleanPreference(DIRECT_DRAW_DISABLED_KEY, systemDefault);
-    }
 
     /**
      * @param value
@@ -384,15 +421,8 @@ public class PreferenceManager implements PropertyManager {
         return get(OVERLAY_ATTRIBUTE_KEY, "LINKING_ID");
     }
 
-    /**
-     * @param value
-     */
-    public void setOverlayAttribute(String value) {
-        put(OVERLAY_ATTRIBUTE_KEY, value);
-    }
-
     public boolean isLinkedSortingEnabled() {
-        return this.getBooleanPreference(ENABLE_LINKED_SORTING, false);
+        return this.getBooleanPreference(ENABLE_LINKED_SORTING, true);
     }
 
     public void setLinkedSortingEnabled(boolean value) {
@@ -524,7 +554,7 @@ public class PreferenceManager implements PropertyManager {
 
         File directory = null;
 
-        String lastFilePath = get(DEFINE_GENOME_INPUT_DIRECTORY_KEY, IGVConstants.getDefaultUserDirectory());
+        String lastFilePath = get(DEFINE_GENOME_INPUT_DIRECTORY_KEY, UIConstants.getUserDirectory().getAbsolutePath());
 
         if (lastFilePath != null) {
             directory = new File(lastFilePath);
@@ -548,7 +578,7 @@ public class PreferenceManager implements PropertyManager {
 
         File genomeImportDirectory = null;
 
-        String lastFilePath = get(LAST_GENOME_IMPORT_DIRECTORY, IGVConstants.getDefaultUserDirectory());
+        String lastFilePath = get(LAST_GENOME_IMPORT_DIRECTORY, UIConstants.getUserDirectory().getAbsolutePath());
 
         if (lastFilePath != null) {
             genomeImportDirectory = new File(lastFilePath);
@@ -574,7 +604,7 @@ public class PreferenceManager implements PropertyManager {
 
         File directory = null;
 
-        String lastFilePath = get(LAST_CYTOBAND_DIRECTORY_KEY, IGVConstants.getDefaultUserDirectory());
+        String lastFilePath = get(LAST_CYTOBAND_DIRECTORY_KEY, UIConstants.getUserDirectory().getAbsolutePath());
 
         if (lastFilePath != null) {
             directory = new File(lastFilePath);
@@ -600,7 +630,7 @@ public class PreferenceManager implements PropertyManager {
 
         File directory = null;
 
-        String lastFilePath = get(LAST_REFFLAT_DIRECTORY_KEY, IGVConstants.getDefaultUserDirectory());
+        String lastFilePath = get(LAST_REFFLAT_DIRECTORY_KEY, UIConstants.getUserDirectory().getAbsolutePath());
 
         if (lastFilePath != null) {
             directory = new File(lastFilePath);
@@ -626,7 +656,7 @@ public class PreferenceManager implements PropertyManager {
 
         File directory = null;
 
-        String lastFilePath = get(LAST_FASTA_DIRECTORY_KEY, IGVConstants.getDefaultUserDirectory());
+        String lastFilePath = get(LAST_FASTA_DIRECTORY_KEY, UIConstants.getUserDirectory().getAbsolutePath());
 
         if (lastFilePath != null) {
             directory = new File(lastFilePath);
@@ -652,7 +682,7 @@ public class PreferenceManager implements PropertyManager {
 
         File directory = null;
 
-        String lastFilePath = get(LAST_SEQUENCE_DIRECTORY_KEY, IGVConstants.getDefaultUserDirectory());
+        String lastFilePath = get(LAST_SEQUENCE_DIRECTORY_KEY, UIConstants.getUserDirectory().getAbsolutePath());
 
         if (lastFilePath != null) {
             directory = new File(lastFilePath);
@@ -730,7 +760,7 @@ public class PreferenceManager implements PropertyManager {
     }
 
     public boolean isPortEnabled() {
-        return getBooleanPreference(PORT_ENABLED, false);
+        return getBooleanPreference(PORT_ENABLED, true);
     }
 
     public boolean isExpandTracks() {
@@ -742,8 +772,7 @@ public class PreferenceManager implements PropertyManager {
      */
     public String getDataServerURL() {
 
-        String masterResourceFile = get(DATA_SERVER_URL_KEY, IGVConstants.DATA_SERVER_REGISTRY).
-                replace("www.broad.mit.edu", "www.broadinstitute.org");
+        String masterResourceFile = get(DATA_SERVER_URL_KEY, DEFAULT_DATA_SERVER_URL);
         return masterResourceFile;
     }
 
@@ -812,7 +841,7 @@ public class PreferenceManager implements PropertyManager {
      * @return
      */
     public String getTrackAttributeName() {
-        return get(TRACK_ATTRIBUTE_NAME_KEY, "SAMPLE");
+        return get(TRACK_ATTRIBUTE_NAME_KEY, "Sample");
     }
 
     /**
@@ -855,7 +884,7 @@ public class PreferenceManager implements PropertyManager {
      */
     public String getDefaultGenome() {
 
-        String genome = get(DEFAULT_GENOME_KEY, "hg18");
+        String genome = get(DEFAULT_GENOME_KEY, null);
         return genome;
     }
 
@@ -906,36 +935,29 @@ public class PreferenceManager implements PropertyManager {
     }
 
     /**
-     * @param height
-     */
-    public void setDefaultTrackHeight(String height) {
-        put(TRACK_HEIGHT_KEY, height);
-    }
-
-    /**
      * @return
      */
     public int getDefaultTrackHeight() {
-
-        int defaultTrackHeight = (int) (Double.parseDouble(
-                PreferenceManager.getInstance().get(
-                        PreferenceManager.TRACK_HEIGHT_KEY,
-                        String.valueOf(INITIAL_TRACK_HEIGHT))));
-
-        return defaultTrackHeight;
+        String key = PreferenceManager.TRACK_HEIGHT_KEY;
+        Number val = (Number) objectCache.get(key);
+        if (val == null) {
+            val = new Double(get(key, String.valueOf(INITIAL_TRACK_HEIGHT)));
+            objectCache.put(key, val);
+        }
+        return val.intValue();
     }
 
     /**
      * @return
      */
     public int getDefaultChartTrackHeight() {
-
-        int defaultTrackHeight = (int) (Double.parseDouble(
-                PreferenceManager.getInstance().get(
-                        PreferenceManager.CHART_TRACK_HEIGHT_KEY,
-                        DEFAULT_CHART_TRACK_HEIGHT)));
-
-        return defaultTrackHeight;
+        String key = PreferenceManager.CHART_TRACK_HEIGHT_KEY;
+        Number val = (Number) objectCache.get(key);
+        if (val == null) {
+            val = new Double(get(key, DEFAULT_CHART_TRACK_HEIGHT));
+            objectCache.put(key, val);
+        }
+        return val.intValue();
     }
 
     /**
@@ -948,7 +970,7 @@ public class PreferenceManager implements PropertyManager {
 
         put(COLOR_SCALE_KEY + type.toString(), colorScaleString);
 
-        // Force cache update
+        // Force cache restorePersistentState
         colorScaleCache.remove(type);
     }
 
@@ -976,6 +998,10 @@ public class PreferenceManager implements PropertyManager {
 
 
     public ContinuousColorScale getColorScale(TrackType type) {
+        if (type == null) {
+            return null;
+        }
+
         ContinuousColorScale scale = colorScaleCache.get(type);
 
         if (scale == null && scaledTypes.contains(type)) {
@@ -996,7 +1022,7 @@ public class PreferenceManager implements PropertyManager {
 
     static Set<String> scaledTypes = new HashSet(Arrays.asList(
             TrackType.LOH, TrackType.RNAI, TrackType.POOLED_RNAI, TrackType.DNA_METHYLATION,
-            TrackType.GENE_EXPRESSION, TrackType.COPY_NUMBER, TrackType.ALLELE_SPECIFIC_COPY_NUMBER));
+            TrackType.GENE_EXPRESSION, TrackType.COPY_NUMBER, TrackType.ALLELE_SPECIFIC_COPY_NUMBER, TrackType.CNV));
 
 
     /**
@@ -1009,14 +1035,10 @@ public class PreferenceManager implements PropertyManager {
     public ContinuousColorScale getDefaultColorScale(TrackType type) {
         switch (type) {
             case LOH:
-                return new ContinuousColorScale(0, -1, 0, 1, Color.red, IGVConstants.LIGHT_YELLOW,
-                        Color.blue);
-
+                return new ContinuousColorScale(0, -1, 0, 1, Color.red, UIConstants.LIGHT_YELLOW, Color.blue);
             case RNAI:
             case POOLED_RNAI:
-                ContinuousColorScale cs = new ContinuousColorScale(0, -3, 0, 3, Color.red,
-                        Color.white,
-                        Color.blue);
+                ContinuousColorScale cs = new ContinuousColorScale(0, -3, 0, 3, Color.red, Color.white, Color.blue);
                 cs.setNoDataColor(new Color(225, 225, 225));
                 return cs;
 
@@ -1026,15 +1048,14 @@ public class PreferenceManager implements PropertyManager {
                 return cs;
 
             case GENE_EXPRESSION:
-                cs = new ContinuousColorScale(-0.1, -1.5, 0.1, 1.5, Color.BLUE, Color.WHITE,
-                        Color.RED);
+                cs = new ContinuousColorScale(-0.1, -1.5, 0.1, 1.5, Color.BLUE, Color.WHITE, Color.RED);
                 cs.setNoDataColor(new Color(225, 225, 225));
                 return cs;
 
             case COPY_NUMBER:
             case ALLELE_SPECIFIC_COPY_NUMBER:
-                return new ContinuousColorScale(-0.1, -1.5, 0.1, 1.5, Color.BLUE, Color.WHITE,
-                        Color.RED);
+            case CNV:
+                return new ContinuousColorScale(-0.1, -1.5, 0.1, 1.5, Color.BLUE, Color.WHITE, Color.RED);
 
             default:
                 return null;
@@ -1044,16 +1065,16 @@ public class PreferenceManager implements PropertyManager {
     /**
      * @param colorScheme
      */
-    public void setMutationColorScheme(Map<Mutation.Type, Color> colorScheme) {
+    public void setMutationColorScheme(ColorTable colorScheme) {
 
         setMutationColorScheme(
-                getcommaSeparatedRGBString(colorScheme.get(Mutation.Type.Indel)),
-                getcommaSeparatedRGBString(colorScheme.get(Mutation.Type.Missense)),
-                getcommaSeparatedRGBString(colorScheme.get(Mutation.Type.Nonsense)),
-                getcommaSeparatedRGBString(colorScheme.get(Mutation.Type.Splice_site)),
-                getcommaSeparatedRGBString(colorScheme.get(Mutation.Type.Synonymous)),
-                getcommaSeparatedRGBString(colorScheme.get(Mutation.Type.Targeted_Region)),
-                getcommaSeparatedRGBString(colorScheme.get(Mutation.Type.Unknown)));
+                getcommaSeparatedRGBString(colorScheme.get("Indel")),
+                getcommaSeparatedRGBString(colorScheme.get("Missense")),
+                getcommaSeparatedRGBString(colorScheme.get("Nonsense")),
+                getcommaSeparatedRGBString(colorScheme.get("Splice_site")),
+                getcommaSeparatedRGBString(colorScheme.get("Synonymous")),
+                getcommaSeparatedRGBString(colorScheme.get("Targeted_Region")),
+                getcommaSeparatedRGBString(colorScheme.get("Unknown")));
     }
 
     /**
@@ -1081,7 +1102,7 @@ public class PreferenceManager implements PropertyManager {
     /**
      * @return
      */
-    public Map<Mutation.Type, Color> getMutationColorScheme() {
+    public ColorTable getMutationColorScheme() {
 
         String indelColor = get(MUTATION_INDEL_COLOR_KEY, null);
         String missenseColor = get(MUTATION_MISSENSE_COLOR_KEY, null);
@@ -1091,57 +1112,49 @@ public class PreferenceManager implements PropertyManager {
         String targetedRegionColor = get(MUTATION_TARGETED_REGION_COLOR_KEY, null);
         String unknownColor = get(MUTATION_UNKNOWN_COLOR_KEY, null);
 
-        HashMap<Mutation.Type, Color> colorScheme = null;
-        if ((indelColor != null) && (missenseColor != null) && (nonsenseColor != null) && (spliceSiteColor != null) && (synonymousColor != null) && (targetedRegionColor != null) && (unknownColor != null)) {
+        ColorTable colorTable = new ColorTable();
+        if ((indelColor != null) && (missenseColor != null) && (nonsenseColor != null) &&
+                (spliceSiteColor != null) &&
+                (synonymousColor != null) &&
+                (targetedRegionColor != null) &&
+                (unknownColor != null)) {
 
-            colorScheme = new HashMap<Mutation.Type, Color>();
 
             String rgb[] = indelColor.split(",");
-            Color color1 = new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]),
-                    Integer.parseInt(rgb[2]));
-            colorScheme.put(Mutation.Type.Indel, color1);
+            Color color1 = new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2]));
+            colorTable.put("Indel", color1);
 
             rgb = missenseColor.split(",");
-            Color color2 = new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]),
-                    Integer.parseInt(rgb[2]));
-            colorScheme.put(Mutation.Type.Missense, color2);
+            Color color2 = new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2]));
+            colorTable.put("Missense", color2);
 
             rgb = nonsenseColor.split(",");
-            Color color3 = new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]),
-                    Integer.parseInt(rgb[2]));
-            colorScheme.put(Mutation.Type.Nonsense, color3);
+            Color color3 = new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2]));
+            colorTable.put("Nonsense", color3);
 
             rgb = spliceSiteColor.split(",");
-            Color color4 = new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]),
-                    Integer.parseInt(rgb[2]));
-            colorScheme.put(Mutation.Type.Splice_site, color4);
+            Color color4 = new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2]));
+            colorTable.put("Splice_site", color4);
 
             rgb = synonymousColor.split(",");
             Color color5 = new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]),
                     Integer.parseInt(rgb[2]));
-            colorScheme.put(Mutation.Type.Synonymous, color5);
+            colorTable.put("Synonymous", color5);
 
             rgb = targetedRegionColor.split(",");
             Color color6 = new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]),
                     Integer.parseInt(rgb[2]));
-            colorScheme.put(Mutation.Type.Targeted_Region, color6);
+            colorTable.put("Targeted_Region", color6);
 
             rgb = unknownColor.split(",");
             Color color7 = new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]),
                     Integer.parseInt(rgb[2]));
-            colorScheme.put(Mutation.Type.Unknown, color7);
+            colorTable.put("Unknown", color7);
         }
 
-        return colorScheme;
+        return colorTable;
     }
 
-    /**
-     * @param inStream
-     * @throws Exception
-     */
-    public void importPreferences(InputStream inStream) throws Exception {
-        Preferences.importPreferences(inStream);
-    }
 
     /**
      */
@@ -1158,20 +1171,11 @@ public class PreferenceManager implements PropertyManager {
         prefs.setDrawAxis(getBooleanPreference(ChartPreferences.Y_AXIS, false));
         prefs.setDrawTrackName(getBooleanPreference(ChartPreferences.DRAW_TRACK_NAME, false));
         prefs.setColorTrackName(getBooleanPreference(ChartPreferences.COLOR_TRACK_NAME, true));
+        prefs.setAutoscale(getBooleanPreference(ChartPreferences.AUTOSCALE, true));
+        prefs.setShowDataRange(getBooleanPreference(ChartPreferences.SHOW_DATA_RANGE, true));
         return prefs;
     }
 
-    /**
-     */
-    public void setChartPreferences(ChartPreferences prefs) {
-        objectCache.put(ChartPreferences.class.getName(), prefs);
-        put(ChartPreferences.DRAW_TOP_BORDER, String.valueOf(prefs.isDrawTopBorder()));
-        put(ChartPreferences.DRAW_BOTTOM_BORDER, String.valueOf(prefs.isDrawBottomBorder()));
-        put(ChartPreferences.COLOR_BORDERS, String.valueOf(prefs.isColorBorders()));
-        put(ChartPreferences.Y_AXIS, String.valueOf(prefs.isDrawAxis()));
-        put(ChartPreferences.DRAW_TRACK_NAME, String.valueOf(prefs.isDrawTrackName()));
-        put(ChartPreferences.COLOR_TRACK_NAME, String.valueOf(prefs.isColorTrackName()));
-    }
 
     /**
      * Class description
@@ -1187,12 +1191,16 @@ public class PreferenceManager implements PropertyManager {
         public static final String Y_AXIS = "CHART.DRAW_AXIS";
         public static final String DRAW_TRACK_NAME = "CHART.DRAW_TRACK_NAME";
         public static final String COLOR_TRACK_NAME = "CHART.COLOR_TRACK_NAME";
+        public static final String AUTOSCALE = "CHART.AUTOSCALE";
+        public static final String SHOW_DATA_RANGE = "CHART.SHOW_DATA_RANGE";
         private boolean drawTopBorder;
         private boolean drawBottomBorder;
         private boolean colorBorders;
         private boolean drawAxis;
         private boolean drawTrackName;
         private boolean colorTrackName;
+        private boolean autoscale;
+        private boolean showDataRange;
 
         public ChartPreferences() {
         }
@@ -1244,19 +1252,47 @@ public class PreferenceManager implements PropertyManager {
         public void setColorTrackName(boolean colorTrackName) {
             this.colorTrackName = colorTrackName;
         }
+
+        public boolean isAutoscale() {
+            return autoscale;
+        }
+
+        public void setAutoscale(boolean autoscale) {
+            this.autoscale = autoscale;
+        }
+
+        public boolean isShowDataRange() {
+            return showDataRange;
+        }
+
+        public void setShowDataRange(boolean showDataRange) {
+            this.showDataRange = showDataRange;
+        }
+    }
+
+
+    public void clearProxySettings() {
+
+        remove(PreferenceManager.USE_PROXY);
+        remove(PreferenceManager.PROXY_HOST);
+        remove(PreferenceManager.PROXY_PORT);
+        remove(PreferenceManager.PROXY_AUTHENTICATE);
+        remove(PreferenceManager.PROXY_USER);
+        remove(PreferenceManager.PROXY_PW);
+        IGVHttpUtils.updateProxySettings();
     }
 
     /**
      */
     public SAMPreferences getSAMPreferences() {
-        SAMPreferences prefs =
-                (SAMPreferences) objectCache.get(SAMPreferences.class.getName());
+        SAMPreferences prefs = (SAMPreferences) objectCache.get(SAMPreferences.class.getName());
         if (prefs == null) {
             prefs = new SAMPreferences();
             objectCache.put(SAMPreferences.class.getName(), prefs);
 
             prefs.setMaxVisibleRange(Float.parseFloat(get(SAMPreferences.MAX_VISIBLE_RANGE, "30")));
             prefs.setShowDuplicates(getBooleanPreference(SAMPreferences.SHOW_DUPLICATES, false));
+            prefs.setShowSoftClipped(getBooleanPreference(SAMPreferences.SHOW_SOFT_CLIPPED, false));
             prefs.setQualityThreshold(Integer.parseInt(get(SAMPreferences.QUALITY_THRESHOLD, "0")));
             prefs.setInsertSizeThreshold(Integer.parseInt(get(SAMPreferences.INSERT_SIZE_THRESHOLD, "10000")));
             prefs.setFlagUnmappedPair(getBooleanPreference(SAMPreferences.FLAG_UNMAPPED_PAIR, false));
@@ -1264,12 +1300,16 @@ public class PreferenceManager implements PropertyManager {
             prefs.setShadeCenter(getBooleanPreference(SAMPreferences.SHADE_CENTER, true));
             prefs.setShowRefSequence(getBooleanPreference(SAMPreferences.SHOW_REF_SEQ, false));
             prefs.setShowCoverageTrack(getBooleanPreference(SAMPreferences.SHOW_COV_TRACK, true));
-            prefs.setMaxLevels(getIntegerPreference(SAMPreferences.MAX_LEVELS, 100000));
+            prefs.setMaxLevels(getIntegerPreference(SAMPreferences.MAX_LEVELS, 100));
+            prefs.setMaxReads(getIntegerPreference(SAMPreferences.MAX_READS, 500000));
             prefs.setShadeBaseQuality(getBooleanPreference(SAMPreferences.SHADE_BASE_QUALITY, true));
             prefs.setBaseQualityMin(getIntegerPreference(SAMPreferences.BASE_QUALITY_MIN, 5));
             prefs.setBaseQualityMax(getIntegerPreference(SAMPreferences.BASE_QUALITY_MAX, 20));
             prefs.setFilterAlignments(getBooleanPreference(SAMPreferences.FILTER_ALIGNMENTS, false));
+            prefs.setFilterFailedReads(getBooleanPreference(SAMPreferences.FILTER_FAILED_READS, true));
+            prefs.setAlleleFreqThreshold(Float.parseFloat(get(SAMPreferences.ALLELE_THRESHOLD, "0.2f")));
             prefs.setFilterURL(get(SAMPreferences.FILTER_URL, null));
+            prefs.setColorBy(get(SAMPreferences.COLOR_BY, null));
         }
 
         return prefs;
@@ -1297,8 +1337,11 @@ public class PreferenceManager implements PropertyManager {
         public static final String MAX_VISIBLE_RANGE = "SAM.MAX_VISIBLE_RANGE";
         public static final String SHOW_ZERO_QUALITY = "SAM.SHOW_VISIBLE_QUALITY";
         public static final String SHOW_DUPLICATES = "SAM.SHOW_DUPLICATES";
+        public static final String SHOW_SOFT_CLIPPED = "SAM.SHOW_SOFT_CLIPPED";
         public static final String FLAG_UNMAPPED_PAIR = "SAM.FLAG_UNMAPPED_PAIR";
         public static final String MAX_LEVELS = "SAM.MAX_LEVELS";
+        public static final String MAX_READS = "SAM.MAX_READS";
+        public static final String ALLELE_THRESHOLD = "SAM.ALLELE_THRESHOLD";
         public static final String QUALITY_THRESHOLD = "SAM.QUALITY_THRESHOLD";
         public static final String INSERT_SIZE_THRESHOLD = "SAM.INSERT_SIZE_THRESHOLD";
         public static final String AUTO_SORT = "SAM.AUTOSORT";
@@ -1310,11 +1353,15 @@ public class PreferenceManager implements PropertyManager {
         public static final String BASE_QUALITY_MAX = "SAM.BASE_QUALITY_MAX";
         public static final String FILTER_ALIGNMENTS = "SAM.FILTER_ALIGNMENTS";
         public static final String FILTER_URL = "SAM.FILTER_URL";
+        public static final String COLOR_BY = "SAM.COLOR_BY";
+        public static final String FILTER_FAILED_READS = "SAM.FILTER_FAILED_READS";
 
         private boolean filterAlignments = false;
+        private boolean filterFailedReads = true;
         private String filterURL;
         private float maxVisibleRange;
-        private int maxLevels;
+        private int maxLevels = 100;
+        private int maxReads = 500000;
         private int qualityThreshold;
         private boolean showDuplicates;
         private boolean flagUnmappedPair;
@@ -1326,6 +1373,9 @@ public class PreferenceManager implements PropertyManager {
         private boolean showRefSequenceFlag;
         private boolean showCoverageTrack;
         private boolean shadeCenterFlag;
+        private float alleleFreqThreshold = 0.2f;
+        private String colorBy = "INSERT_SIZE";
+        private boolean showSoftClipped = false;
 
         /**
          * @return the maxVisibleRange
@@ -1507,5 +1557,46 @@ public class PreferenceManager implements PropertyManager {
         public void setFilterURL(String filterURL) {
             this.filterURL = filterURL;
         }
+
+        public int getMaxReads() {
+            return maxReads;
+        }
+
+        public void setMaxReads(int maxReads) {
+            this.maxReads = maxReads;
+        }
+
+        public float getAlleleFreqThreshold() {
+            return alleleFreqThreshold;
+        }
+
+        public void setAlleleFreqThreshold(float alleleFreqThreshold) {
+            this.alleleFreqThreshold = alleleFreqThreshold;
+        }
+
+        public String getColorBy() {
+            return colorBy;
+        }
+
+        public void setColorBy(String colorBy) {
+            this.colorBy = colorBy;
+        }
+
+
+        public boolean isFilterFailedReads() {
+            return filterFailedReads;
+        }
+
+        public void setFilterFailedReads(boolean filterFailedReads) {
+            this.filterFailedReads = filterFailedReads;
+        }
+
+        public boolean isShowSoftClipped() {
+            return showSoftClipped;
+        }
+
+        public void setShowSoftClipped(boolean showSoftClipped) {
+            this.showSoftClipped = showSoftClipped;
+        }
     }
 }
diff --git a/src/org/broad/igv/das/DASConnection.java b/src/org/broad/igv/das/DASConnection.java
deleted file mode 100644
index 26cde5d..0000000
--- a/src/org/broad/igv/das/DASConnection.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
-Copyright (c) 2007-$today.year by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
-All Rights Reserved.
-
-This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
-is available at http://www.opensource.org/licenses/lgpl-2.1.php.
-
-THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
-ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
-OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
-RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
-ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
-DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
-BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
-FOREGOING.
-*/
-
-package org.broad.igv.das;
-
-import org.broad.igv.exceptions.DataLoadException;
-import org.broad.igv.util.ResourceLocator;
-import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Arrays;
-
-public class DASConnection implements DASSource {
-
-
-    Document dasDoc;
-    URL dasServer;
-    String[] capabilities;
-    String host;
-
-    public DASConnection(ResourceLocator locator) {
-
-        try {
-            URL serverURL = new URL(locator.getPath());
-            String tmp = serverURL.toExternalForm();
-            if (tmp.endsWith("/"))
-                serverURL = new URL(tmp.substring(0, tmp.length() - 1));
-
-            //"http://das.sanger.ac.uk/das/cosmic_mutations"
-            startConnection(serverURL);
-        } catch (MalformedURLException mfu) {
-            throw new DataLoadException("The URL supplied is invalid", locator.getPath());
-        }
-    }
-
-    private void startConnection(URL serverURL) {
-
-        try {
-            HttpURLConnection serverConnection = (HttpURLConnection) serverURL.openConnection();
-            serverConnection.setConnectTimeout(7000);
-//            server = serverConnection.getHeaderField("X-DAS-Server");
-//            version = serverConnection.getHeaderField("X-DAS-Version");
-            String headerCaps = serverConnection.getHeaderField("X-DAS-Capabilities");
-            capabilities = headerCaps.split(";");
-            host = serverURL.getPath();
-            if (host.length() > 20) {
-                host = host.substring(0, 20) + "..";
-            }
-            dasServer = serverURL;
-            serverConnection.disconnect();
-
-        } catch (IOException ioe) {
-            throw new DataLoadException("Could not complete connection to server", serverURL.toExternalForm());
-        }
-    }
-
-    public boolean isCapable(String capability) {
-
-        if (capabilities != null) {
-            for (String a : Arrays.asList(capabilities)) {
-                a.toLowerCase();
-                if (a.contains(capability.toLowerCase()) && a != null) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    private void findFeatures(String chrom, int start, int stop) throws MalformedURLException {
-
-        if (isCapable("features")) {
-            URL dataQuery = new URL(dasServer.toExternalForm() + "/features?" + "segment=" + chrom + ":" + start + "," + stop);
-            createDocument(dataQuery);
-        } else {
-            throw new DataLoadException("Could not connect to a DAS Capable Server", dasServer.toExternalForm());
-        }
-    }
-
-    private void createDocument(URL url) {
-        try {
-            Document dom = createDocument(connectServer(url));
-            dasDoc = dom;
-        } catch (ParserConfigurationException pce) {
-            throw new DataLoadException("A problem has occured with the parser", url.toExternalForm());
-        } catch (SAXException se) {
-            throw new DataLoadException("A problem has occured during SAX", url.toExternalForm());
-        } catch (IOException ioe) {
-            throw new DataLoadException("A problem has occured with the data", url.toExternalForm());
-        }
-    }
-
-    public Document getDasDocument(String chrom, int start, int end) throws MalformedURLException {
-
-        findFeatures(chrom, start, end);
-        if (dasDoc != null)
-            return dasDoc;
-        else
-            throw new DataLoadException("No data was recovered", dasServer.toExternalForm());
-    }
-
-    public String getName() {
-        if (host != null)
-            return host;
-        else
-            return "DAS-generic";
-    }
-
-    private InputStream connectServer(URL url) {
-        try {
-            HttpURLConnection serverConnection = (HttpURLConnection) url.openConnection();
-//            System.out.println("url = " + url.toExternalForm());
-            serverConnection.setConnectTimeout(7000);
-            serverConnection.connect();
-            InputStream xmlStream = serverConnection.getInputStream();
-            return xmlStream;
-        } catch (IOException ioe) {
-            throw new DataLoadException("Connection Timeout", url.toExternalForm());
-        }
-    }
-
-    private Document createDocument(InputStream inputStream)
-            throws ParserConfigurationException, IOException, SAXException {
-        DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
-        return documentBuilder.parse(inputStream);
-    }
-}
\ No newline at end of file
diff --git a/src/org/broad/igv/das/DASFeatureParser.java b/src/org/broad/igv/das/DASFeatureParser.java
deleted file mode 100644
index dfa9d7c..0000000
--- a/src/org/broad/igv/das/DASFeatureParser.java
+++ /dev/null
@@ -1,251 +0,0 @@
-package org.broad.igv.das;
-
-import org.broad.igv.exceptions.DataLoadException;
-import org.broad.igv.feature.*;
-import org.broad.igv.track.FeatureTrack;
-import org.broad.igv.track.TrackProperties;
-import org.broad.igv.ui.IGVModel;
-import org.broad.igv.util.AsciiLineReader;
-import org.broad.igv.util.ResourceLocator;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-import org.w3c.dom.traversal.DocumentTraversal;
-import org.w3c.dom.traversal.NodeFilter;
-import org.w3c.dom.traversal.TreeWalker;
-
-import java.awt.*;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/*
-Copyright (c) 2007-$today.year by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
-All Rights Reserved.
-
-This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
-is available at http://www.opensource.org/licenses/lgpl-2.1.php.
-
-THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
-ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
-OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
-RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
-ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
-DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
-BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
-FOREGOING.
-*/
-
-public class DASFeatureParser extends AbstractFeatureParser {
-
-
-    private TrackProperties trackProperties = null;
-    private List<Feature> features = new ArrayList<Feature>();
-    private String[] chroms = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16",
-            "17", "18", "19", "20", "21", "22", "X", "Y", "M"};
-    //    private String[] chroms = {"7"};
-    private int maxFeatures = -1;
-    private String currentChrom = "";
-
-
-    protected Feature parseLine(String nextLine) {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
-    }
-
-    public List<FeatureTrack> loadTracks(ResourceLocator locator, DASSource source) {
-
-        FeatureTrack track = new FeatureTrack(locator, "empty");
-        loadFeatures(track, locator, source);
-
-        if (features.size() < 1)
-            throw new DataLoadException("No features were found", locator.toString());
-
-        track.initFeatures(features);
-        FeatureDB.addFeatures(features);
-        parsingComplete(features);
-        track.setMinimumHeight(35);
-        track.setHeight(45);
-        if (trackProperties != null) {
-            track.setTrackProperties(trackProperties);
-        }
-
-        List<FeatureTrack> tracks = new ArrayList();
-        tracks.add(track);
-        return tracks;
-    }
-
-    public List<FeatureTrack> loadTracks(ResourceLocator locator, String chrom, DASSource source) {
-
-        chroms = new String[]{chrom};
-        return loadTracks(locator, source);
-    }
-
-    public void loadTracks(AsciiLineReader reader) {
-
-    }
-
-    public void setMaxFeatures(int maxFeatures) {
-        this.maxFeatures = maxFeatures;
-    }
-
-
-    /**
-     * Load features for all chromosomes.
-     * TODO:  This is basically hardcoded for human, or at least specieces with 24 chromosomes numbered 1-22,
-     * TODO:  X, Y, and M.  THis needs to be fixed before release.
-     *
-     * @param track
-     * @param locator
-     * @param source
-     */
-    public void loadFeatures(FeatureTrack track, ResourceLocator locator, DASSource source) {
-
-        track.setDisplayName(source.getName());
-        for (String chrom : chroms) {
-            query(source, chrom, 1, Integer.MAX_VALUE);
-        }
-    }
-
-
-    //   TODO:  Also fix exception handling.   Should be logged at a minimum, and possible rethrown.
-    public void query(DASSource source, String chrom, int start, int end) {
-        try {
-            currentChrom = chrom;
-
-            Genome genome = IGVModel.getInstance().getViewContext().getGenome();
-            String igvChr = genome == null ? chrom : genome.getChromosomeAlias(chrom);
-
-            Document dasdoc = source.getDasDocument(chrom, start, end);
-            try {
-                DocumentTraversal traversal = (DocumentTraversal) dasdoc;
-                TreeWalker treewalker = traversal.createTreeWalker(
-                        dasdoc.getDocumentElement(), NodeFilter.SHOW_ELEMENT, null, true);
-                gatherNodes(treewalker, "FEATURE", igvChr);
-
-            } catch (Exception ex) {
-                ex.printStackTrace();
-            }
-        } catch (IOException ioe) {
-            ioe.printStackTrace();
-        }
-    }
-
-    private void gatherNodes(TreeWalker walker,
-                             String tag,
-                             String igvChr) {
-
-        Node parent = walker.getCurrentNode();
-        int featureCount = 0;
-        if (maxFeatures == -1)
-            maxFeatures = Integer.MAX_VALUE;
-
-        for (Node n = walker.firstChild(); n != null && featureCount < maxFeatures;
-             n = walker.nextSibling(), featureCount++) {
-
-            if (((Element) n).getTagName().equalsIgnoreCase(tag)) {
-                features.add(processFeature(walker, igvChr));
-                continue;
-            }
-            gatherNodes(walker, tag, igvChr);
-        }
-        walker.setCurrentNode(parent);
-    }
-
-    private BasicFeature processFeature(TreeWalker walker, String igvChr) {
-
-        String start = "0", end = "0", orientation = "";
-        String id = "", label = "", description = "", type = "", link = "", method = "";
-        Strand strand = Strand.NONE;
-        Node parent = walker.getCurrentNode();
-        Node tmpNode;
-
-        //GET THE FEATURE ATTRIBUTES
-        NamedNodeMap nnm = parent.getAttributes();
-        tmpNode = nnm.getNamedItem("id");
-        id = tmpNode.getTextContent();
-        tmpNode = nnm.getNamedItem("label");
-        label = tmpNode.getTextContent();
-
-        //GO THROUGH FEATURE NODES
-        for (Node n = walker.firstChild(); n != null;
-             n = walker.nextSibling()) {
-
-            if (((Element) n).getTagName().equalsIgnoreCase("START")) {
-                start = n.getTextContent();
-            }
-            if (((Element) n).getTagName().equalsIgnoreCase("END")) {
-                end = n.getTextContent();
-            }
-            if (((Element) n).getTagName().equalsIgnoreCase("ORIENTATION")) {
-                orientation = n.getTextContent();
-                if (orientation.equals("-")) {
-                    strand = Strand.NEGATIVE;
-                } else if (orientation.equalsIgnoreCase("+")) {
-                    strand = Strand.POSITIVE;
-                }
-            }
-            if (((Element) n).getTagName().equalsIgnoreCase("NOTE")) {
-                description = n.getTextContent();
-            }
-            if (((Element) n).getTagName().equalsIgnoreCase("TYPE")) {
-                type = n.getTextContent();
-            }
-            if (((Element) n).getTagName().equalsIgnoreCase("LINK")) {
-                NamedNodeMap tmpnnm = n.getAttributes();
-                Node tmpnode = tmpnnm.getNamedItem("href");
-                link = tmpnode.getTextContent();
-            }
-            if (((Element) n).getTagName().equalsIgnoreCase("METHOD")) {
-                method = n.getTextContent();
-            }
-        }
-
-        //SET FEATURE ATTRIBUTES
-        walker.setCurrentNode(parent);
-        if (Integer.parseInt(end) - Integer.parseInt(start) == 0) {
-            end = String.valueOf(Integer.parseInt(start) + 1);
-        }
-
-        BasicFeature feature = new BasicFeature(igvChr, Integer.parseInt(start), Integer.parseInt(end) + 1);
-        label = label.replace("?", " ");
-        description = description.replace("&nbsp;", "<br>");
-        description = description.replace("<br><br><br>", "<br>");
-        description = description.replace("<br><br>", "<br>");
-        description = description.replace(":", ":&nbsp;");
-        description = description.replace("?", " ");
-        description = description + "<br>TYPE:&nbsp;" + type;
-        feature.setName(label);
-        feature.setDescription(description);
-        feature.setLink(link);
-        feature.setIdentifier("\n");
-
-
-        //TODO Makde another Color panel on the preferences for the different feature colors
-        //TODO Would like to base it on one of the kuler panels
-        //Using Colors from http://kuler.adobe.com/#themeID/659786
-        if (type.equalsIgnoreCase("substitution")) {
-            feature.setColor(new Color(0, 140, 255));
-//            feature.setColor(Mutation.getColorScheme().get(Mutation.Type.Splice_site));
-        } else if (type.equalsIgnoreCase("insertion") || type.equalsIgnoreCase("deletion")) {
-            feature.setColor(new Color(85, 217, 0));
-//            feature.setColor(Mutation.getColorScheme().get(Mutation.Type.Indel));
-        } else {
-            feature.setColor(new Color(255, 128, 0));
-//            feature.setColor(Mutation.getColorScheme().get(Mutation.Type.Nonsense));
-        }
-        return feature;
-    }
-
-    public static DASFeatureParser getInstanceFor(String filename) {
-
-        //TODO Connect to the server, get name of the server
-        //TODO Open up a new DASConnection
-        //TODO Do not parse the data yet
-        return new DASFeatureParser();
-
-    }
-
-
-}
diff --git a/src/org/broad/igv/das/DASFeatureSource.java b/src/org/broad/igv/das/DASFeatureSource.java
new file mode 100644
index 0000000..3f53ef8
--- /dev/null
+++ b/src/org/broad/igv/das/DASFeatureSource.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+package org.broad.igv.das;
+
+import org.apache.log4j.Logger;
+import org.broad.igv.exceptions.DataLoadException;
+import org.broad.igv.feature.*;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.track.FeatureSource;
+import org.broad.igv.track.tribble.CachingFeatureReader;
+import org.broad.igv.ui.WaitCursorManager;
+import org.broad.igv.ui.util.MessageUtils;
+import org.broad.igv.util.IGVHttpUtils;
+import org.broad.igv.util.LRUCache;
+import org.broad.igv.util.ResourceLocator;
+import org.broad.tribble.FeatureReader;
+import org.broad.tribble.util.CloseableTribbleIterator;
+import org.w3c.dom.*;
+import org.w3c.dom.traversal.DocumentTraversal;
+import org.w3c.dom.traversal.NodeFilter;
+import org.w3c.dom.traversal.TreeWalker;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+
+
+//http://das.sanger.ac.uk/das/cosmic_mutations/features
+//http://genome.ucsc.edu/cgi-bin/das/hg16/features?segment=1:1,100000;type=refGene
+
+public class DASFeatureSource implements FeatureSource {
+
+    private static Logger log = Logger.getLogger(DASFeatureSource.class);
+    private static final IteratorWrapper EMPTY__ITERATOR = new IteratorWrapper(new ArrayList<Feature>().iterator());
+
+    private String path;
+    private String serverURL;
+    private boolean isValid = true;
+    CachingFeatureReader reader;
+    int binSize = 250000;
+    private String type;
+    String[] parameters;
+    Map<String, BasicFeature> groupFeatureCache = new HashMap(10000);
+
+
+
+    public DASFeatureSource(ResourceLocator locator) throws MalformedURLException {
+        URL url = new URL(locator.getPath());
+        String host = url.getHost();
+        String protocol = url.getProtocol();
+        path = url.getPath();
+        serverURL = protocol + "://" + host + path;
+
+        String paramString = url.getQuery();
+        if (paramString != null) {
+            parameters = paramString.split(";");
+
+            for (String param : parameters) {
+                if (param.startsWith("type=")) {
+                    type = param.substring(5);
+                }
+            }
+        }
+
+        if (locator.getPath().contains("genome.ucsc.edu") && type == null) {
+            throw new DataLoadException("<html>Feature type is required for UCSC DAS tracks. <br>" +
+                    "See http://www.broadinstitute.org/igv/LoadData for more details.",
+                    locator.getPath());
+        }
+
+        reader = new CachingFeatureReader(new DasReader(), binSize);
+    }
+
+    public Iterator<Feature> getFeatures(String chr, int start, int end) throws IOException {
+        return reader.query(chr, start, end);
+    }
+
+    public List<LocusScore> getCoverageScores(String chr, int i, int i1, int zoom) {
+        return null;
+    }
+
+    public int getBinSize() {
+        return binSize;
+    }
+
+    public void setBinSize(int size) {
+        reader.setTileSize(size);
+
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+
+    class DasReader implements FeatureReader {
+
+
+        public CloseableTribbleIterator query(String chr, int start, int end, boolean contained) throws IOException {
+            return query(chr, start, end);
+        }
+
+        public CloseableTribbleIterator query(String chr, int start, int end) throws IOException {
+
+            String dasChr = chr.startsWith("chr") ? chr.substring(3, chr.length()) : chr;
+            int dasStart = start + 1;
+            int dasEnd = end;
+
+            if (isValid && !chr.equals("All")) {
+                WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
+                try {
+                    groupFeatureCache.clear();
+                    List<Feature> features = gatherFeatures(dasChr, dasStart, dasEnd);
+                    groupFeatureCache.clear();
+                    if (features.size() < 1) {
+                        return EMPTY__ITERATOR;
+                    }
+                    return new IteratorWrapper(features.iterator());
+                } finally {
+                    WaitCursorManager.removeWaitCursor(token);
+                }
+            }
+            return EMPTY__ITERATOR;
+        }
+
+        // TODO Iterating not permitted -- throw exception?
+
+        public CloseableTribbleIterator iterator() throws IOException {
+            return null;
+        }
+
+
+        /**
+         * Query for features in the given interval.  The coordinates are translated to DAS conventions.
+         * <p/>
+         * An end value <= 0 will result in a query for features over the whole chromosome.
+         *
+         * @param chr
+         * @param start
+         * @param end
+         * @return
+         */
+        private List<Feature> gatherFeatures(String chr, int start, int end) {
+
+            List<Feature> features = new ArrayList<Feature>();
+            try {
+
+                String urlString = serverURL + "?" + "segment=" + chr;
+                if (end > 0) urlString += ":" + start + "," + end;
+
+                // Add parameters
+                if (parameters != null) {
+                    for (String param : parameters) {
+                        if (!param.startsWith("segment")) {
+                            urlString += ";" + param;
+                        }
+                    }
+                }
+
+                URL dataQuery = new URL(urlString);
+                Document dom = getDocument(dataQuery);
+                if (dom == null) {
+                    return Collections.emptyList();
+                }
+                parseDocument(dom, chr, features);
+                FeatureUtils.sortFeatureList(features);
+                return features;
+
+            } catch (IOException ioe) {
+                throw new DataLoadException("Failed to reconnect with server", serverURL);
+            }
+        }
+
+
+        private Document getDocument(URL query) {
+            InputStream is = null;
+            try {
+                HttpURLConnection serverConnection = IGVHttpUtils.openConnection(query);
+                serverConnection.connect();
+                is = serverConnection.getInputStream();
+                return createDocument(is);
+            } catch (Exception e) {
+                isValid = false;
+                log.error(e);
+                MessageUtils.showMessage("<html>The DAS Server: " + serverURL + " has returned an error or invalid data." +
+                        "<br>" + e.getMessage());
+                return null;
+            }
+            finally {
+                if (is != null) {
+                    try {
+                        is.close();
+                    } catch (IOException e) {
+                        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+                    }
+                }
+            }
+        }
+
+        private Document createDocument(InputStream inputStream)
+                throws ParserConfigurationException, IOException, SAXException {
+            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+            dbf.setValidating(false);
+            DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
+
+            documentBuilder.setEntityResolver(new EntityResolver() {
+                public InputSource resolveEntity(String publicId, String systemId)
+                        throws SAXException, IOException {
+                    System.out.println("Ignoring " + publicId + ", " + systemId);
+                    return new InputSource(new StringReader(""));
+                }
+            });
+
+            /*BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
+            String nextLine;
+            while((nextLine = br.readLine()) != null) {
+                System.out.println("** " + nextLine);
+            }
+            */
+
+            Document dom = documentBuilder.parse(inputStream);
+            inputStream.close();
+            return dom;
+        }
+
+        public String getPath() {
+            return serverURL;
+        }
+
+        private void parseDocument(Document dasDoc, String chr, List<Feature> features) {
+
+            try {
+                DocumentTraversal traversal = (DocumentTraversal) dasDoc;
+                TreeWalker treewalker = traversal.createTreeWalker(
+                        dasDoc.getDocumentElement(), NodeFilter.SHOW_ELEMENT, null, true);
+                parseTree(treewalker, "FEATURE", chr, features);
+
+            } catch (Exception ex) {
+                log.error(ex);
+                throw new DataLoadException("Error loading DAS resource (" + ex.toString() + ")", getPath());
+            }
+        }
+
+        private List<Feature> parseTree(TreeWalker walker,
+                                        String tag,
+                                        String chr, List<Feature> features) {
+
+            Node parent = walker.getCurrentNode();
+            Node n = walker.firstChild();
+            while (n != null) {
+                if (((Element) n).getTagName().equalsIgnoreCase(tag)) {
+                    Feature f = getFeature(walker, chr);
+                    if (f != null) {
+                        features.add(f);
+                    }
+
+                    n = walker.nextSibling();
+//                if (features.size() > 16384){
+//                    break;
+//                }
+                    continue;
+                }
+                parseTree(walker, tag, chr, features);
+                n = walker.nextSibling();
+            }
+            walker.setCurrentNode(parent);
+            return features;
+        }
+
+        private BasicFeature getFeature(TreeWalker walker, String chr) {
+
+            String id;
+            String label;
+
+            String type = "";
+            int start = 0;
+            int end = 0;
+            float score;
+            Strand strand = Strand.NONE;
+
+            int phase;
+            String description = null;
+            String link = null;
+            String group = null;
+            String groupLabel = null;
+            String groupLink = null;
+
+
+            Node featureNode = walker.getCurrentNode();
+
+            //GET THE FEATURE ATTRIBUTES
+            NamedNodeMap nnm = featureNode.getAttributes();
+            Node tmpNode = nnm.getNamedItem("id");
+            id = tmpNode.getTextContent();
+            tmpNode = nnm.getNamedItem("label");
+            label = tmpNode == null ? "" : tmpNode.getTextContent();
+
+            //GO THROUGH FEATURE NODES
+            for (Node n = walker.firstChild(); n != null;
+                 n = walker.nextSibling()) {
+
+                if (((Element) n).getTagName().equalsIgnoreCase("TYPE")) {
+                    type = n.getTextContent();
+                } else if (((Element) n).getTagName().equalsIgnoreCase("START")) {
+                    start = Integer.parseInt(n.getTextContent()) - 1;
+                } else if (((Element) n).getTagName().equalsIgnoreCase("END")) {
+                    end = Math.max(start + 1, Integer.parseInt(n.getTextContent()));
+                } else if (((Element) n).getTagName().equalsIgnoreCase("SCORE")) {
+                    String scoreString = n.getTextContent();
+                    if (!scoreString.equals("-")) score = Float.parseFloat(scoreString);
+                } else if (((Element) n).getTagName().equalsIgnoreCase("PHASE")) {
+                    String phaseString = n.getTextContent();
+                    if (!phaseString.equals("-")) phase = Integer.parseInt(n.getTextContent());
+                } else if (((Element) n).getTagName().equalsIgnoreCase("ORIENTATION")) {
+                    String orientation = n.getTextContent();
+                    if (orientation.equals("-")) {
+                        strand = Strand.NEGATIVE;
+                    } else if (orientation.equalsIgnoreCase("+")) {
+                        strand = Strand.POSITIVE;
+                    }
+                } else if (((Element) n).getTagName().equalsIgnoreCase("NOTE")) {
+                    if (description == null) {
+                        description = "<html>" + n.getTextContent();
+                    } else {
+                        description += ("<br>" + n.getTextContent());
+                    }
+                } else if (((Element) n).getTagName().equalsIgnoreCase("GROUP")) {
+                    nnm = n.getAttributes();
+                    tmpNode = nnm.getNamedItem("id");
+                    group = tmpNode.getTextContent();
+
+                    tmpNode = nnm.getNamedItem("label");
+                    if (tmpNode != null) {
+                        groupLabel = tmpNode.getTextContent();
+                    }
+
+                    NodeList linkNodes = ((Element) n).getElementsByTagName("LINK");
+                    if (linkNodes.getLength() > 0) {
+                        Node ln = linkNodes.item(0);
+                        Node hrefNode = ln.getAttributes().getNamedItem("href");
+                        if (hrefNode != null) {
+                            groupLink = hrefNode.getTextContent();
+                        }
+                    }
+                    // TODO  group Note elements
+
+                } else if (((Element) n).getTagName().equalsIgnoreCase("LINK")) {
+                    NamedNodeMap tmpnnm = n.getAttributes();
+                    Node tmpnode = tmpnnm.getNamedItem("href");
+                    link = tmpnode.getTextContent();
+                }
+
+            }
+
+
+            walker.setCurrentNode(featureNode);
+
+
+            BasicFeature feature = null;
+            if (group != null) {
+                Exon exon = new Exon(chr, start, end, strand);
+
+                feature = groupFeatureCache.get(group);
+                if (feature == null) {
+                    feature = new BasicFeature(exon.getChr(), exon.getStart(), exon.getEnd(), exon.getStrand());
+                    feature.addExon(exon);
+                    if (groupLink != null) {
+                        feature.setURL(groupLink);
+                    }
+                    if (groupLabel != null) {
+                        feature.setName(groupLabel);
+                    } else {
+                        feature.setName(label);
+                    }
+                    groupFeatureCache.put(group, feature);
+                } else { // Seen before, just add the exon and exit
+                    feature.addExon(exon);
+                    return null;
+                }
+
+            } else {
+                feature = new BasicFeature(chr, start, end);
+                if (link != null) {
+                    feature.setURL(link);
+                }
+                feature.setIdentifier(id);
+                label = label.replace("?", " ");
+                feature.setName(label);
+                feature.setType(type);
+                feature.setStrand(strand);
+            }
+
+
+            if (description != null) {
+                description = description.replace("&nbsp;", "<br>");
+                description = description.replace("<br><br><br>", "<br>");
+                description = description.replace("<br><br>", "<br>");
+                description = description.replace(":", ":&nbsp;");
+                description = description.replace("?", " ");
+                description = description + "<br>TYPE:&nbsp;" + type;
+                feature.setDescription(description);
+            }
+
+
+            return feature;
+        }
+
+        public void close() throws IOException {
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        public Set<String> getSequenceNames() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+    }
+
+
+    static class IteratorWrapper<Feature> implements CloseableTribbleIterator {
+
+        private Iterator<Feature> iterator;
+
+        IteratorWrapper(Iterator<Feature> iterator) {
+            this.iterator = iterator;
+        }
+
+        public void close() {
+
+        }
+
+        public Iterator<Feature> iterator() {
+            return iterator;
+        }
+
+        public boolean hasNext() {
+            return iterator.hasNext();  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        public Feature next() {
+            return iterator.next();
+        }
+
+        public void remove() {
+            iterator.remove();
+        }
+    }
+}
diff --git a/src/org/broad/igv/das/DASFileSource.java b/src/org/broad/igv/das/DASFileSource.java
deleted file mode 100644
index b0eff93..0000000
--- a/src/org/broad/igv/das/DASFileSource.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package org.broad.igv.das;
-
-import org.apache.log4j.Logger;
-import org.broad.igv.ui.util.MessageUtils;
-import org.broad.igv.util.ParsingUtils;
-import org.broad.igv.util.ResourceLocator;
-import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.*;
-import java.net.MalformedURLException;
-
-/**
- * A DASSource for a group of files, separated by chromosome, in a directory.   The directory can be local or
- * accessible by http.  The files must follow the naming convention <chromosome>.das.
- */
-public class DASFileSource implements DASSource {
-
-    private static Logger log = Logger.getLogger(DASFileSource.class);
-
-    String rootPath;
-
-    /**
-     * Construct a DASFileSource and set the rootPath.  It is assumed that the locator represents
-     * one of the das files and the root path is its parent.
-     * //TODO -- this is an ugly convention and is just asking for trouble.   redo this.
-     *
-     * @param locator
-     */
-    public DASFileSource(ResourceLocator locator) {
-
-        if (locator.getPath().startsWith("http")) {
-            int idx = locator.getPath().lastIndexOf('/');
-            rootPath = locator.getPath().substring(0, idx);
-        } else {
-            rootPath = (new File(locator.getPath())).getParent();
-        }
-    }
-
-    public boolean isCapable(String capability) {
-        return true;
-    }
-
-    public Document getDasDocument(String chrom, int start, int stop) throws MalformedURLException {
-
-        String filePath = rootPath + "/" + chrom + ".das";
-        InputStream is = null;
-
-        try {
-            ResourceLocator rl = new ResourceLocator(filePath);
-            is = ParsingUtils.openInputStream(rl);
-            return createDocument(new BufferedInputStream(is));
-
-        } catch (FileNotFoundException e) {
-            // This is not neccessarily an error,
-            log.info("DAS data file not found: " + filePath);
-        }
-        catch (Exception e) {
-            MessageUtils.showMessage("Error loading DAS file: filePath" + e.getMessage());
-            log.error("Error loading DAS data", e);
-        }
-
-        return null;
-
-
-    }
-
-    public String getName() {
-        return rootPath;
-    }
-
-    private Document createDocument(InputStream inputStream)
-            throws ParserConfigurationException, IOException, SAXException {
-        DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
-        return documentBuilder.parse(inputStream);
-    }
-}
diff --git a/src/org/broad/igv/das/DASSource.java b/src/org/broad/igv/das/DASSource.java
deleted file mode 100644
index 4cdc465..0000000
--- a/src/org/broad/igv/das/DASSource.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package org.broad.igv.das;
-
-import org.w3c.dom.Document;
-
-import java.net.MalformedURLException;
-
-/**
- * Created by IntelliJ IDEA.
- * User: jrobinso
- * Date: Dec 15, 2009
- * Time: 1:58:05 PM
- * To change this template use File | Settings | File Templates.
- */
-public interface DASSource {
-    
-    boolean isCapable(String capability);
-
-    Document getDasDocument(String chrom, int start, int stop) throws MalformedURLException;;
-
-    String getName();
-}
diff --git a/src/org/broad/igv/das/DASUtils.java b/src/org/broad/igv/das/DASUtils.java
deleted file mode 100644
index 5fa33e0..0000000
--- a/src/org/broad/igv/das/DASUtils.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package org.broad.igv.das;
-
-import java.io.*;
-import java.net.URL;
-
-/**
- * Created by IntelliJ IDEA.
- * User: jrobinso
- * Date: Dec 15, 2009
- * Time: 1:41:55 PM
- * To change this template use File | Settings | File Templates.
- */
-public class DASUtils {
-
-
-    private static String[] chroms = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16",
-            "17", "18", "19", "20", "21", "22", "X", "Y", "M"};
-
-    public static void dumpDASDocuments(File directory, String dasURL) throws IOException {
-
-
-        for (String chr : chroms) {
-            File file = new File(directory, chr + ".das");
-            URL dataQuery = new URL(dasURL + "/features?segment=" + chr);
-            InputStream is = dataQuery.openStream();
-            BufferedInputStream bis = new BufferedInputStream(is);
-            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
-            int b = 0;
-            while ((b = bis.read()) >= 0) {
-                bos.write(b);
-            }
-            bos.flush();
-            bos.close();
-            is.close();
-        }
-    }
-
-
-    public static void main(String[] args) throws IOException {
-
-        File dir = new File("cosmic");
-        if (!dir.exists()) {
-            dir.mkdir();
-        }
-        dumpDASDocuments(dir, "http://das.sanger.ac.uk/das/cosmic_mutations/features");
-    }
-}
diff --git a/src/org/broad/igv/data/AbstractDataSource.java b/src/org/broad/igv/data/AbstractDataSource.java
index efbf20c..d4f70bf 100644
--- a/src/org/broad/igv/data/AbstractDataSource.java
+++ b/src/org/broad/igv/data/AbstractDataSource.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -20,20 +20,18 @@ package org.broad.igv.data;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
-import org.broad.igv.feature.FeatureUtils;
-import org.broad.igv.feature.GeneManager;
-import org.broad.igv.feature.LocusScore;
-import org.broad.igv.preprocess.old.FeatureBin;
-import org.broad.igv.preprocess.old.FeatureBinCalculator;
+import org.broad.igv.feature.*;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.tdf.Bin;
 import org.broad.igv.track.TrackType;
 import org.broad.igv.track.WindowFunction;
-import org.broad.igv.ui.IGVModel;
+import org.broad.igv.util.LRUCache;
 import org.broad.igv.util.ObjectCache;
-import org.broad.igv.tdf.Bin;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Collections;
 
 /**
  * @author jrobinso
@@ -45,9 +43,10 @@ public abstract class AbstractDataSource implements DataSource {
     // DataManager dataManager;
     boolean cacheSummaryTiles = true;
     WindowFunction windowFunction = WindowFunction.mean;
-    ObjectCache<String, SummaryTile> summaryTileCache = new ObjectCache();
+    LRUCache<String, SummaryTile> summaryTileCache = new LRUCache(this, 10);
 
     // abstract DataManager getDataManager();
+
     /**
      * Return the number of precomputed zoom levels for the given chromosome.
      *
@@ -57,6 +56,7 @@ public abstract class AbstractDataSource implements DataSource {
     abstract protected int getNumZoomLevels(String chr);
 
     // abstract protected TrackType getTrackType();
+
     /**
      * Return "raw" (i.e. not summarized) data for the specified interval.
      *
@@ -65,7 +65,7 @@ public abstract class AbstractDataSource implements DataSource {
      * @param endLocation
      * @return
      */
-    abstract protected DataTile getRawData(String chr, int startLocation, int endLocation, int zoom);
+    abstract protected DataTile getRawData(String chr, int startLocation, int endLocation);
 
     /**
      * Return the precomputed summary tiles for the given locus and zoom level.  If
@@ -82,20 +82,10 @@ public abstract class AbstractDataSource implements DataSource {
         return null;
     }
 
-    /**
-     * Return the median value for the chromosome, zoom level, and window function.
-     * This is used to nromalize some types of data in heatmaps.
-     */
-    abstract public double getMedian(int zoom, String chr);
 
-    /**
-     * Method description
-     *
-     * @param chr
-     * @return
-     */
     public int getChrLength(String chr) {
-        return IGVModel.getInstance().getViewContext().getChromosomeLength();
+        return ViewContext.getInstance().getChromosomeLength();
+
     }
 
     /**
@@ -118,13 +108,25 @@ public abstract class AbstractDataSource implements DataSource {
      * @param chr
      * @return
      */
-    public int getLongestFeature(String chr) {
-        return (getTrackType() == TrackType.GENE_EXPRESSION) ? 2000000 : 1000;
-    }
+    public abstract int getLongestFeature(String chr);
+
+    //{
+    //
+    //    if (getTrackType() == TrackType.GENE_EXPRESSION) {
+    //        String genomeId = ViewContext.getInstance().getGenomeId();
+    //        GeneManager gm = GeneManager.getGeneManager(genomeId);
+    //        return (gm == null) ? 1000000 : gm.getLongestGeneLength(chr);
+    //    } else {
+    //        return 1000;
+    //    }
+    //}
 
 
-    public List<LocusScore> getSummaryScoresForRange(String chr, int startLocation,
-                                                     int endLocation, int zoom) {
+    public List<LocusScore> getSummaryScoresForRange
+            (String
+                    chr, int startLocation,
+             int endLocation,
+             int zoom) {
 
 
         List<SummaryTile> tiles = getSummaryTilesForRange(chr, startLocation, endLocation, zoom);
@@ -140,23 +142,21 @@ public abstract class AbstractDataSource implements DataSource {
         return summaryScores;
     }
 
-    private List<SummaryTile> getSummaryTilesForRange(String chr, int startLocation,
-                                                      int endLocation, int zoom) {
+    private List<SummaryTile> getSummaryTilesForRange(String chr, int startLocation,int endLocation, int zoom) {
         assert endLocation >= startLocation;
 
-        int startTile = 0;
-        int endTile = 0;
+        int startTile;
+        int endTile;
         if (zoom < getNumZoomLevels(chr)) {
             return getPrecomputedSummaryTiles(chr, startLocation, endLocation, zoom);
         } else {
             int chrLength = getChrLength(chr);
+            if (chrLength == 0) {
+                return Collections.emptyList();
+            }
             endLocation = Math.min(endLocation, chrLength);
 
-            // Get some extra tiles to be sure we get long features that start
-            // before startLocaion
-            int longestFeature = getLongestFeature(chr);
-
-            int adjustedStart = Math.max(0, startLocation - longestFeature);
+            int adjustedStart = Math.max(0, startLocation);
             int adjustedEnd = Math.min(chrLength, endLocation);
 
             int z = zoom; //Math.min(8, zoom);
@@ -170,7 +170,20 @@ public abstract class AbstractDataSource implements DataSource {
             for (int t = startTile; t <= endTile; t++) {
                 int tileStart = (int) (t * tileWidth);
                 int tileEnd = Math.min(chrLength, (int) ((t + 1) * tileWidth));
-                SummaryTile summaryTile = computeSummaryTile(chr, t, tileStart, tileEnd, zoom);
+
+                String key = chr + "_" + zoom + "_" + t + getWindowFunction();
+                SummaryTile summaryTile = summaryTileCache.get(key);
+                if (summaryTile == null) {
+                    summaryTile = computeSummaryTile(chr, t, tileStart, tileEnd);
+
+                    if (this.cacheSummaryTiles) {
+                        synchronized (summaryTileCache) {
+                            summaryTileCache.put(key, summaryTile);
+                        }
+                    }
+                }
+
+
                 if (summaryTile != null) {
                     tiles.add(summaryTile);
                 }
@@ -181,123 +194,101 @@ public abstract class AbstractDataSource implements DataSource {
     }
 
     /**
-     *
      * Note:  Package scope used so this method can be unit tested
+     *
      * @param chr
      * @param tileNumber
      * @param startLocation
      * @param endLocation
-     * @param zoom
      * @return
      */
-    SummaryTile computeSummaryTile(String chr, int tileNumber, int startLocation, int endLocation, int zoom) {
 
-        String key = chr + "_" + zoom + "_" + tileNumber + getWindowFunction();
-        SummaryTile tile = summaryTileCache.get(key);
+    SummaryTile computeSummaryTile(String chr, int tileNumber, int startLocation, int endLocation) {
 
-        if (tile == null) {
 
-            // Get a window   prior to start to be sure we get all features.
-            String genomeId = IGVModel.getInstance().getViewContext().getGenomeId();
+        // TODO -- we should use an index here
+        int longestGene = getLongestFeature(chr);
 
-            GeneManager gm = GeneManager.getGeneManager(genomeId);
+        int adjustedStart = Math.max(startLocation - longestGene, 0);
+        DataTile rawTile = getRawData(chr, adjustedStart, endLocation);
+        SummaryTile tile = null;
 
-            // TODO -- we should use an index here
-            int longestGene = (gm == null) ? 1000000 : gm.getLongestGeneLength(chr);
-            int adjustedStart = Math.max(startLocation - longestGene, 0);
-            DataTile rawTile = getRawData(chr, adjustedStart, endLocation, zoom);
+        int nBins = Math.min(700, endLocation - startLocation);
+        if ((rawTile != null) && !rawTile.isEmpty() && nBins > 0) {
 
-            int nBins = Math.min(700, endLocation - startLocation);
-            if ((rawTile != null) && !rawTile.isEmpty() && nBins > 0) {
+            tile = new SummaryTile(tileNumber, startLocation);
+            double binSize = ((float) (endLocation - startLocation)) / nBins;
+            Bin[] bins = new Bin[nBins];
 
-                tile = new SummaryTile(tileNumber, startLocation);
-                double binSize = ((float) (endLocation - startLocation)) / nBins;
-                Bin[] bins = new Bin[nBins];
 
+            int[] starts = rawTile.getStartLocations();
+            int[] ends = rawTile.getEndLocations();
+            float[] values = rawTile.getValues();
+            String[] features = rawTile.getFeatureNames();
 
-                int[] starts = rawTile.getStartLocations();
-                int[] ends = rawTile.getEndLocations();
-                float[] values = rawTile.getValues();
-                String[] features = rawTile.getFeatureNames();
+            List<LocusScore> scores = new ArrayList(700);
 
-                List<LocusScore> scores = new ArrayList(700);
+            for (int i = 0; i < starts.length; i++) {
+                int s = starts[i];
+                int e = ends == null ? s + 1 : Math.max(s + 1, ends[i]);
 
-                for (int i = 0; i < starts.length; i++) {
-                    int s = starts[i];
-                    int e = ends == null ? s+1 : Math.max(s + 1, ends[i]);
-
-                    if (e < startLocation) {
-                        continue;
-                    } else if (s > endLocation) {
-                        break;
-                    }
+                if (e < startLocation) {
+                    continue;
+                } else if (s > endLocation) {
+                    break;
+                }
 
-                    String probeName = features == null ? null : features[i];
-                    float v = values[i];
+                String probeName = features == null ? null : features[i];
+                float v = values[i];
 
 
-                    int startBin = (int) Math.max(0, ((s - startLocation) / binSize));
-                    int endBin = (int) Math.min(bins.length - 1, ((e - startLocation) / binSize));
-                    for (int b = startBin; b <= endBin; b++) {
-                        Bin bin = bins[b];
-                        if (bin == null) {
-                            int start = (int) (startLocation + b * binSize);
-                            int end = (int) (startLocation + (b + 1) * binSize);
-                            bins[b] = new Bin(start, end, probeName, v, windowFunction);
-                        } else {
-                            bin.addValue(probeName, v);
-                        }
+                int startBin = (int) Math.max(0, ((s - startLocation) / binSize));
+                int endBin = (int) Math.min(bins.length, ((e - startLocation) / binSize));
+                if(startBin == endBin && endBin < bins.length) {
+                    endBin++;
+                }
+                for (int b = startBin; b < endBin; b++) {
+                    Bin bin = bins[b];
+                    if (bin == null) {
+                        int start = (int) (startLocation + b * binSize);
+                        int end = (int) (startLocation + (b + 1) * binSize);
+                        bins[b] = new Bin(start, end, probeName, v, windowFunction);
+                    } else {
+                        bin.addValue(probeName, v);
                     }
-
                 }
+            }
 
 
-                // Aggregate adjacent bins.  This stiches back together features that span multiple bins.
-                // TODO-- look at computing variable length bins to start with
+            // Aggregate adjacent bins.  This stiches back together features that span multiple bins.
+            // TODO-- look at computing variable length bins to start with
 
-                Bin currentBin = null;
-                for (int b = 0; b < bins.length; b++) {
-                    if (bins[b] != null) {
-                        if (currentBin == null) {
-                            currentBin = bins[b];
+            Bin currentBin = null;
+            for (int b = 0; b < bins.length; b++) {
+                if (bins[b] != null) {
+                    if (currentBin == null) {
+                        currentBin = bins[b];
+                    } else {
+                        if (currentBin.isExtension(bins[b])) {
+                            currentBin.setEnd(bins[b].getEnd());
                         } else {
-                            if (currentBin.isExtension(bins[b])) {
-                                currentBin.setEnd(bins[b].getEnd());
-                            } else {
-                                scores.add(currentBin);
-                                currentBin = bins[b];
-                            }
+                            scores.add(currentBin);
+                            currentBin = bins[b];
                         }
                     }
                 }
-                if (currentBin != null) {
-                    scores.add(currentBin);
-                }
-
-                tile.addAllScores(scores);
             }
-
-            if (this.cacheSummaryTiles) {
-                synchronized (summaryTileCache) {
-                    summaryTileCache.put(key, tile);
-                }
+            if (currentBin != null) {
+                scores.add(currentBin);
             }
+
+            tile.addAllScores(scores);
         }
 
         return tile;
     }
 
 
-    private static List<FeatureBin> computeBins(int startLocation, int endLocation,
-                                                List<LocusScore> features) {
-        double binSize = IGVModel.getInstance().getViewContext().getScale();
-        int nBins = (int) Math.ceil((endLocation - startLocation) / binSize);
-        double correctedBinSize = ((double) (endLocation - startLocation)) / nBins;
-        return (new FeatureBinCalculator()).computeFeatureBins(features, nBins, correctedBinSize,
-                startLocation, endLocation);
-    }
-
-
     /**
      * Return true if the data has been log normalized.
      *
@@ -307,20 +298,12 @@ public abstract class AbstractDataSource implements DataSource {
         return true;
     }
 
-    /**
-     * Method description
-     *
-     * @param statType
-     */
+
     public void setWindowFunction(WindowFunction statType) {
         this.windowFunction = statType;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public WindowFunction getWindowFunction() {
         return windowFunction;
     }
@@ -338,11 +321,7 @@ public abstract class AbstractDataSource implements DataSource {
 
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public Collection<WindowFunction> getAvailableWindowFunctions() {
         return wfs;
     }
diff --git a/src/org/broad/igv/data/BasicScore.java b/src/org/broad/igv/data/BasicScore.java
index 5ffe2d3..9142c03 100644
--- a/src/org/broad/igv/data/BasicScore.java
+++ b/src/org/broad/igv/data/BasicScore.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -57,6 +57,10 @@ public class BasicScore implements LocusScore {
         return chr;
     }
 
+    public String getChr() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
     public int getStart() {
         return start;
     }
diff --git a/src/org/broad/igv/data/Bin.java b/src/org/broad/igv/data/Bin.java
new file mode 100644
index 0000000..1c36803
--- /dev/null
+++ b/src/org/broad/igv/data/Bin.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.broad.igv.data;
+
+/**
+ * @author jrobinso
+ */
+public interface Bin {
+
+    public int getStart();
+
+    public int getFeatureCount();
+
+}
diff --git a/src/org/broad/igv/data/CharArrayList.java b/src/org/broad/igv/data/CharArrayList.java
index 8551f60..0d731dc 100644
--- a/src/org/broad/igv/data/CharArrayList.java
+++ b/src/org/broad/igv/data/CharArrayList.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/data/ChromosomeData.java b/src/org/broad/igv/data/ChromosomeData.java
index 58e563b..1d6e13c 100644
--- a/src/org/broad/igv/data/ChromosomeData.java
+++ b/src/org/broad/igv/data/ChromosomeData.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -36,6 +36,8 @@ public class ChromosomeData {
 
     private int[] endLocations;
 
+    private String[] probes;
+
     private Map<String, float[]> data;
 
     ChromosomeData(String chr) {
@@ -66,4 +68,12 @@ public class ChromosomeData {
     public void setEndLocations(int[] endLocations) {
         this.endLocations = endLocations;
     }
+
+    public String[] getProbes() {
+        return probes;
+    }
+
+    public void setProbes(String[] probes) {
+        this.probes = probes;
+    }
 }
diff --git a/src/org/broad/igv/data/ChromosomeSummary.java b/src/org/broad/igv/data/ChromosomeSummary.java
index d97c324..fed6713 100644
--- a/src/org/broad/igv/data/ChromosomeSummary.java
+++ b/src/org/broad/igv/data/ChromosomeSummary.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/data/CompositeScore.java b/src/org/broad/igv/data/CompositeScore.java
index 6c5ccde..458907a 100644
--- a/src/org/broad/igv/data/CompositeScore.java
+++ b/src/org/broad/igv/data/CompositeScore.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -78,51 +78,30 @@ public class CompositeScore implements LocusScore {
         this.score = sc.score;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public CompositeScore copy() {
         return new CompositeScore(this);
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public float getScore() {
 
         // Todo - implementation
         return score;
     }
 
-    /**
-     * Method description
-     *
-     * @param confidence
-     */
+
     public void setConfidence(float confidence) {
 
         // ignore
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public float getConfidence() {
         return 1.0f;
     }
 
-    /**
-     * Method description
-     *
-     * @param windowFunction
-     * @return
-     */
+
     public String getValueString(double position, WindowFunction windowFunction) {
         if (scores == null) {
             return "";
@@ -139,29 +118,21 @@ public class CompositeScore implements LocusScore {
         return buf.toString();
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+    public String getChr() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+
     public int getStart() {
         return start;
     }
 
-    /**
-     * Method description
-     *
-     * @param start
-     */
+
     public void setStart(int start) {
         this.start = start;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public int getEnd() {
         return end;
     }
diff --git a/src/org/broad/igv/data/DataSource.java b/src/org/broad/igv/data/DataSource.java
index c3235d0..59c5e24 100644
--- a/src/org/broad/igv/data/DataSource.java
+++ b/src/org/broad/igv/data/DataSource.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -35,9 +35,9 @@ import java.util.List;
  */
 public interface DataSource {
 
-    double getDataMax(String chr);
+    double getDataMax();
 
-    double getDataMin(String chr);
+    double getDataMin();
 
     List<LocusScore> getSummaryScoresForRange(
             String chr,
diff --git a/src/org/broad/igv/data/DataStatistics.java b/src/org/broad/igv/data/DataStatistics.java
index 8b1aca3..956561b 100644
--- a/src/org/broad/igv/data/DataStatistics.java
+++ b/src/org/broad/igv/data/DataStatistics.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/data/DataTile.java b/src/org/broad/igv/data/DataTile.java
index 926c226..c1de7fc 100644
--- a/src/org/broad/igv/data/DataTile.java
+++ b/src/org/broad/igv/data/DataTile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/data/DataTile2D.java b/src/org/broad/igv/data/DataTile2D.java
index 0f332a7..babaa50 100644
--- a/src/org/broad/igv/data/DataTile2D.java
+++ b/src/org/broad/igv/data/DataTile2D.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/data/DataUtils.java b/src/org/broad/igv/data/DataUtils.java
index 88e4e9a..a85cd0b 100644
--- a/src/org/broad/igv/data/DataUtils.java
+++ b/src/org/broad/igv/data/DataUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -135,42 +135,23 @@ public class DataUtils {
 
         private double estBytesPerLine;
 
-        /**
-         * Constructs ...
-         *
-         * @param estRowCount
-         * @param columnCount
-         * @param estBytesPerLine
-         */
         public AsciiFileMetrics(int estRowCount, int columnCount, double estBytesPerLine) {
             this.estRowCount = estRowCount;
             this.columnCount = columnCount;
             this.estBytesPerLine = estBytesPerLine;
         }
 
-        /**
-         * Method description
-         *
-         * @return
-         */
+
         public double getEstBytesPerLine() {
             return estBytesPerLine;
         }
 
-        /**
-         * Method description
-         *
-         * @return
-         */
+
         public int getEstRowCount() {
             return estRowCount;
         }
 
-        /**
-         * Method description
-         *
-         * @return
-         */
+
         public int getColumnCount() {
             return columnCount;
         }
diff --git a/src/org/broad/igv/data/Dataset.java b/src/org/broad/igv/data/Dataset.java
index caf21d1..87beced 100644
--- a/src/org/broad/igv/data/Dataset.java
+++ b/src/org/broad/igv/data/Dataset.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -61,4 +61,5 @@ public interface Dataset {
     public TrackProperties getTrackProperties();
 
 
+    Integer getLongestFeature(String chr);
 }
diff --git a/src/org/broad/igv/data/DatasetDataSource.java b/src/org/broad/igv/data/DatasetDataSource.java
index bf33711..6882ba8 100644
--- a/src/org/broad/igv/data/DatasetDataSource.java
+++ b/src/org/broad/igv/data/DatasetDataSource.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -26,7 +26,7 @@ package org.broad.igv.data;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
+import org.broad.igv.Globals;
 import org.broad.igv.feature.Chromosome;
 import org.broad.igv.feature.Genome;
 import org.broad.igv.track.TrackType;
@@ -57,17 +57,18 @@ public class DatasetDataSource extends AbstractDataSource {
         this.dataset = dataset;
 
         // TODO -- remove this "instanceof" hack
-        if (dataset instanceof IGVDataset) {
-            genomeSummaryData = ((IGVDataset) dataset).getGenomeSummary();
-        } else {
-            genomeSummaryData = new GenomeSummaryData(genome, new String[] {trackId});
-            for (Chromosome chr : genome.getChromosomes()) {
-                int[] startLocations = dataset.getStartLocations(chr.getName());
-                if (!chr.getName().equals(IGVConstants.CHR_ALL) && (startLocations != null)
-                            && (startLocations.length > 0)) {
-                    Map<String, float[]> dMap = new HashMap();
-                    dMap.put(trackId, dataset.getData(trackId, chr.getName()));
-                    genomeSummaryData.addData(chr.getName(), startLocations, dMap);
+        if (genome.getHomeChromosome().equals(Globals.CHR_ALL)) {
+            if (dataset instanceof IGVDataset) {
+                genomeSummaryData = ((IGVDataset) dataset).getGenomeSummary();
+            } else {
+                genomeSummaryData = new GenomeSummaryData(genome, new String[]{trackId});
+                for (Chromosome chr : genome.getChromosomes()) {
+                    int[] startLocations = dataset.getStartLocations(chr.getName());
+                    if (!chr.getName().equals(Globals.CHR_ALL) && (startLocations != null) && (startLocations.length > 0)) {
+                        Map<String, float[]> dMap = new HashMap();
+                        dMap.put(trackId, dataset.getData(trackId, chr.getName()));
+                        genomeSummaryData.addData(chr.getName(), startLocations, dMap);
+                    }
                 }
             }
         }
@@ -84,13 +85,13 @@ public class DatasetDataSource extends AbstractDataSource {
     }
 
     @Override
-    protected DataTile getRawData(String chr, int startLocation, int endLocation, int zoom) {
+    protected DataTile getRawData(String chr, int startLocation, int endLocation) {
         int[] startLocs = null;
         int[] endLocs = null;
         float[] data = null;
         String[] features = null;
 
-        if (chr.equals(IGVConstants.CHR_ALL)) {
+        if (chr.equals(Globals.CHR_ALL) && genomeSummaryData != null) {
             startLocs = genomeSummaryData.getLocations();
             endLocs = startLocs;
             data = genomeSummaryData.getData(trackId);
@@ -148,11 +149,6 @@ public class DatasetDataSource extends AbstractDataSource {
     }
 
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public TrackType getTrackType() {
         try {
             return dataset.getType();
@@ -162,46 +158,24 @@ public class DatasetDataSource extends AbstractDataSource {
         }
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     @Override
     public boolean isLogNormalized() {
         return dataset.isLogNormalized();
     }
 
-    /**
-     * Method description
-     *
-     * @param chr
-     * @return
-     */
-    public double getDataMax(String chr) {
+
+    public double getDataMax() {
         return dataset.getDataMax();
     }
 
-    /**
-     * Method description
-     *
-     * @param chr
-     * @return
-     */
-    public double getDataMin(String chr) {
+
+    public double getDataMin() {
         return dataset.getDataMin();
     }
 
-    /**
-     * Method description
-     *
-     * @param zoom
-     * @param chr
-     * @return
-     */
-    public double getMedian(int zoom, String chr) {
-        return 1.0;
+    @Override
+    public int getLongestFeature(String chr) {
+        return dataset.getLongestFeature(chr);
     }
-
-
 }
diff --git a/src/org/broad/igv/data/ExpressionProbe.java b/src/org/broad/igv/data/ExpressionProbe.java
index 7205353..411b735 100644
--- a/src/org/broad/igv/data/ExpressionProbe.java
+++ b/src/org/broad/igv/data/ExpressionProbe.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -53,7 +53,7 @@ public class ExpressionProbe implements Comparable {
 
     public ExpressionProbe(Feature feature) {
         this(feature.getName());
-        this.chr = feature.getChromosome();
+        this.chr = feature.getChr();
         this.start = feature.getStart();
         this.end = feature.getEnd();
     }
diff --git a/src/org/broad/igv/data/FeatureBin.java b/src/org/broad/igv/data/FeatureBin.java
new file mode 100644
index 0000000..ab08e7b
--- /dev/null
+++ b/src/org/broad/igv/data/FeatureBin.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * Bin.java
+ *
+ * Created on October 29, 2007, 3:40 PM
+ *
+ * To change this template, choose Tools | Template Manager
+ * and open the template in the editor.
+ */
+package org.broad.igv.data;
+
+import org.broad.igv.feature.LocusScore;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a tile bin.
+ * start -- start location of the bin
+ * features --  list of features that partially or fully overlap the bin
+ * <p/>
+ * // TODO = this is a mix of two types, a feature type bin and snp type bin.
+ * //        the feature bin uses the "features" collection, snp type uses
+ * startIndex / endIndex.
+ * //        refactor to combine both or split.
+ *
+ * @author jrobinso
+ */
+public class FeatureBin implements Bin {
+
+    private int start;
+    private List<LocusScore> features;
+
+    public FeatureBin(int location) {
+        this.start = location;
+        features = new ArrayList();
+    }
+
+    public void addFeature(LocusScore feature) {
+        features.add(feature);
+    }
+
+    public List<LocusScore> getFeatures() {
+        return features;
+    }
+
+    public int getFeatureCount() {
+
+        return getFeatures().size();
+
+    }
+
+    /**
+     * Return the scores for this bin after removing all NaN values.  If
+     * there are no scores return null;
+     *
+     * @return
+     */
+    public float[] getFeatureScores() {
+
+        int nScores = 0;
+        for (LocusScore f : features) {
+            if (!Float.isNaN(f.getScore())) {
+                nScores++;
+            }
+        }
+
+        if (nScores > 0) {
+            float[] scores = new float[nScores];
+            int scoreIndex = 0;
+            for (LocusScore f : features) {
+                float s = f.getScore();
+                if (!Float.isNaN(s)) {
+                    scores[scoreIndex] = s;
+                    scoreIndex++;
+                }
+            }
+            return scores;
+        } else {
+            return null;
+        }
+    }
+
+    public int getStart() {
+        return start;
+    }
+}
+
diff --git a/src/org/broad/igv/data/FeatureBinCalculator.java b/src/org/broad/igv/data/FeatureBinCalculator.java
new file mode 100644
index 0000000..7f6cf33
--- /dev/null
+++ b/src/org/broad/igv/data/FeatureBinCalculator.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.data;
+
+import org.broad.igv.feature.LocusScore;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.TreeMap;
+
+/**
+ * @author jrobinso
+ */
+public class FeatureBinCalculator {
+
+    /**
+     * Allocates features to FeatureBins.  FeatureBins are equally spaced and begin at startLocation.
+     *
+     * @param features
+     * @param nFeatureBins
+     * @param startLocation
+     * @param endLocation
+     * @return
+     */
+    public List<FeatureBin> computeFeatureBins(List<? extends LocusScore> features, int nFeatureBins, double binSize, int startLocation, int endLocation) {
+
+        TreeMap<Integer, FeatureBin> featureBins = new TreeMap();
+
+        // Allocate features to FeatureBins
+        for (LocusScore feature : features) {
+            int binStart = (int) ((feature.getStart() - startLocation) / binSize);
+            int binEnd = (int) ((feature.getEnd() - startLocation) / binSize);
+            if (binEnd >= 0 && binStart < nFeatureBins) {
+                int lastBin = Math.min(binEnd + 1, nFeatureBins);
+                for (int b = Math.max(0, binStart); b < lastBin; b++) {
+                    // We know this is a feature bin.  Ugly, but no time to redesign this
+                    FeatureBin fBin = featureBins.get(b);
+                    if (fBin == null) {
+                        int location = (int) (startLocation + b * binSize);
+                        fBin = new FeatureBin(location);
+                        featureBins.put(b, fBin);
+                    }
+                    fBin.addFeature(feature);
+                }
+            }
+        }
+
+        Collection c = featureBins.values();
+        return new ArrayList(featureBins.values());
+    }
+
+
+}
diff --git a/src/org/broad/igv/data/FloatArrayList.java b/src/org/broad/igv/data/FloatArrayList.java
deleted file mode 100644
index ec23d77..0000000
--- a/src/org/broad/igv/data/FloatArrayList.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.data;
-
-/**
- * @author jrobinso
- */
-public class FloatArrayList {
-
-    static final int maxGrowIncrement = Integer.MAX_VALUE / 10;
-    int size = 0;
-    float[] values;
-
-    public FloatArrayList(int maxSize) {
-        values = new float[maxSize];
-    }
-
-    public void add(float v) {
-        if (size >= values.length) {
-            grow();
-        }
-        values[size] = v;
-        size++;
-    }
-
-    public void addAll(float[] array) {
-        if (values.length - size < array.length) {
-            grow(array.length);
-        }
-        System.arraycopy(array, 0, values, size, array.length);
-        size += array.length;
-    }
-
-    public float[] toArray() {
-        trim();
-        return values;
-    }
-
-    public int getSize() {
-        return size;
-    }
-
-    public void clear() {
-        size = 0;
-    }
-
-    private void grow() {
-        grow((int) (Math.max(1000, 0.2 * values.length)));
-    }
-
-    private void grow(int increment) {
-        if (values.length >= Integer.MAX_VALUE) {
-            throw new RuntimeException("Maximum array size exceeded");
-        }
-        int newSize = Math.min(Integer.MAX_VALUE, values.length + increment);
-        resize(newSize);
-
-    }
-
-    private void resize(int newSize) {
-        float[] tmp = new float[newSize];
-        System.arraycopy(values, 0, tmp, 0, Math.min(tmp.length, values.length));
-        values = tmp;
-    }
-
-    private void trim() {
-        resize(size);
-    }
-
-    public void reorder(int[] indeces) {
-        if (indeces.length != size) {
-            throw new IllegalArgumentException(
-                    "Index array length not equal to size");
-        }
-        float[] reorderedValues = new float[size];
-        for (int i = 0; i < size; i++) {
-            reorderedValues[i] = values[indeces[i]];
-        }
-        values = reorderedValues;
-    }
-}
diff --git a/src/org/broad/igv/data/GCTDataConsumer.java b/src/org/broad/igv/data/GCTDataConsumer.java
index cdedd1e..e1a6574 100644
--- a/src/org/broad/igv/data/GCTDataConsumer.java
+++ b/src/org/broad/igv/data/GCTDataConsumer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/data/GCTDataset.java b/src/org/broad/igv/data/GCTDataset.java
index 779d671..c773020 100644
--- a/src/org/broad/igv/data/GCTDataset.java
+++ b/src/org/broad/igv/data/GCTDataset.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -26,12 +26,12 @@
  */
 package org.broad.igv.data;
 
-import static org.broad.igv.IGVConstants.CHR_ALL;
+import org.broad.igv.Globals;
 import org.broad.igv.feature.Genome;
 import org.broad.igv.feature.GenomeManager;
-import org.broad.igv.util.ParsingUtils;
 import org.broad.igv.track.TrackProperties;
 import org.broad.igv.track.TrackType;
+import org.broad.igv.util.ParsingUtils;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -60,6 +60,8 @@ public class GCTDataset implements Dataset, GCTDataConsumer {
     Map<String, int[]> startLocationMap = new HashMap();
     Map<String, int[]> endLocationMap = new HashMap();
 
+    private Map<String, Integer> longestFeatureMap;
+
     /**
      * Map of chromosome -> array of data values
      */
@@ -86,6 +88,7 @@ public class GCTDataset implements Dataset, GCTDataConsumer {
 
 
     // Todo -- implement
+
     public float getDataMin() {
         return -3f;
     }
@@ -154,7 +157,7 @@ public class GCTDataset implements Dataset, GCTDataConsumer {
 
     public int[] getEndLocations(String chr) {
 
-        if (chr.equals(CHR_ALL)) {
+        if (chr.equals(Globals.CHR_ALL)) {
             return null;
         }
 
@@ -162,6 +165,7 @@ public class GCTDataset implements Dataset, GCTDataConsumer {
     }
 
     // Not relevant to feature datasets
+
     public int getWindowSpan() {
         return -1;
     }
@@ -207,10 +211,16 @@ public class GCTDataset implements Dataset, GCTDataConsumer {
         return trackProperties;
     }
 
-    /**
-     * @param trackProperties the trackProperties to set
-     */
     public void setTrackLine(String trackLine) {
         ParsingUtils.parseTrackLine(trackLine, trackProperties);
     }
+
+    public Integer getLongestFeature(String chr) {
+        return longestFeatureMap == null ? 1000 :
+                longestFeatureMap.containsKey(chr) ? longestFeatureMap.get(chr) : 1;
+    }
+
+    public void setLongestFeatureMap(Map<String, Integer> longestFeatureMap) {
+        this.longestFeatureMap = longestFeatureMap;
+    }
 }
diff --git a/src/org/broad/igv/data/GCTDatasetParser.java b/src/org/broad/igv/data/GCTDatasetParser.java
index cbdb5eb..7478d8e 100644
--- a/src/org/broad/igv/data/GCTDatasetParser.java
+++ b/src/org/broad/igv/data/GCTDatasetParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -29,15 +29,14 @@ package org.broad.igv.data;
 
 //~--- non-JDK imports --------------------------------------------------------
 
-import cern.colt.list.ObjectArrayList;
+import org.broad.igv.util.collections.IntArrayList;
 import org.apache.log4j.Logger;
 import org.broad.igv.exceptions.ParserException;
 import org.broad.igv.exceptions.ProbeMappingException;
 import org.broad.igv.feature.*;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.tools.StatusMonitor;
-import org.broad.igv.track.TrackManager;
 import org.broad.igv.track.TrackType;
-import org.broad.igv.ui.IGVModel;
 import org.broad.igv.util.AsciiLineReader;
 import org.broad.igv.util.ParsingUtils;
 import org.broad.igv.util.ResourceLocator;
@@ -75,15 +74,12 @@ public class GCTDatasetParser {
     int probeColumn;
     int descriptionColumn;
     String genome;
-    ProbeSet probeSet;
-    /**
-     * Flag to record server connect failures
-     */
-    boolean canConnectToServer = true;
+
     /**
-     * Flag to record connect load user probe mapping file
+     * Map chr -> longest feature
      */
-    boolean canLoadProbeMapping = true;
+    Map<String, Integer> longestProbeMap;
+
     /**
      * Map colum heading -> index for effecient reverse lookup
      */
@@ -104,14 +100,11 @@ public class GCTDatasetParser {
 
         this.dataFileLocator = resFile;
         this.genome = genome;
-        if (IGVModel.getInstance().getViewContext().getGenomeId() != null && IGVModel.getInstance().getViewContext().getGenomeId().equals(genome)) {
-            this.geneManager = TrackManager.getInstance().getGeneManager();
-        } else {
-            this.geneManager = GeneManager.getGeneManager(genome);
-        }
+        this.geneManager = GeneManager.getGeneManager(genome);
+        longestProbeMap = new HashMap();
 
         if (probeFile != null) {
-             BEDFileParser parser = new BEDFileParser();
+            BEDFileParser parser = new BEDFileParser();
             ResourceLocator rl = new ResourceLocator(probeFile);
             AsciiLineReader reader = ParsingUtils.openAsciiReader(rl);
             List<Feature> features = parser.loadFeatures(reader);
@@ -137,7 +130,8 @@ public class GCTDatasetParser {
 
     /*
      */
-    public static boolean parsableMAGE_TAB(ResourceLocator file) {
+
+    public static boolean parsableMAGE_TAB(ResourceLocator file) throws IOException {
         AsciiLineReader reader = null;
         try {
             reader = ParsingUtils.openAsciiReader(file);
@@ -160,9 +154,6 @@ public class GCTDatasetParser {
                 }
                 return true;
             }
-        } catch (IOException e) {
-            e.printStackTrace();
-            return false;
         } finally {
             if (reader != null) {
                 reader.close();
@@ -183,7 +174,7 @@ public class GCTDatasetParser {
      *
      * @return
      */
-    public void parse(GCTDataConsumer dataset) {
+    public void parse(GCTDataset dataset) {
 
         // Create a buffer for the string split utility.  We use  a custom utility as opposed
         // to String.split() for performance.
@@ -274,11 +265,11 @@ public class GCTDatasetParser {
             int nTokens = ParsingUtils.split(headerLine, tokens, '\t');
 
             int nColumns = (nTokens - dataStartColumn) / skip;
-            ObjectArrayList columnHeadingsObj = new ObjectArrayList();
+            ArrayList columnHeadingsObj = new ArrayList();
             for (int i = 0; i < nColumns; i++) {
                 String heading = tokens[dataStartColumn + i * skip].replace('\"', ' ').trim();
                 if (type == FileType.MAGE_TAB) {
-                    if (!columnHeadingsObj.contains(heading, true)) {
+                    if (!columnHeadingsObj.contains(heading)) {
                         columnHeadingsObj.add(heading);
                         headingIndexMap.put(heading, columnHeadingsObj.size() - 1);
                     }
@@ -294,6 +285,7 @@ public class GCTDatasetParser {
             nColumns = columnHeadings.length;
 
             //parse quantitation type column header
+            
             IntArrayList valuesIndices = new IntArrayList(nColumns);
             if (type == FileType.MAGE_TAB) {
                 nextLine = reader.readLine();
@@ -415,25 +407,20 @@ public class GCTDatasetParser {
             }
         }
 
-        if ((dataset == null) || ((GCTDataset) dataset).isEmpty()) {
-            String genome = IGVModel.getInstance().getViewContext().getGenome().getId();
+        dataset.setLongestFeatureMap(longestProbeMap);
+
+        if ((dataset == null) || dataset.isEmpty()) {
+            String genome = ViewContext.getInstance().getGenome().getId();
             throw new ProbeMappingException(fn, genome);
         }
     }
 
-    /**
-     * Method descriptionå
-     *
-     * @param probeId
-     * @param description
-     * @param values
-     * @param calls
-     */
+
     public void addRow(String probeId, String description, float[] values, char[] calls) {
 
-        if (log.isDebugEnabled()) {
-            log.debug("Adding row for probe: " + probeId);
-        }
+        //if (log.isDebugEnabled()) {
+        //    log.debug("Adding row for probe: " + probeId);
+        //}
 
         // TODO -- simplify this with the use of a locus interface
         String chr = null;
@@ -446,14 +433,14 @@ public class GCTDatasetParser {
             String[] locusStrings = getExplicitLocusStrings(description);
             if (locusStrings != null) {
 
-                if (log.isDebugEnabled()) {
-                    String tmp = "";
-                    for (String ls : locusStrings) {
-                        tmp += ls;
-                    }
-                    log.debug("Found locus string in description: " + probeId + " -> " + tmp);
-
-                }
+                //if (log.isDebugEnabled()) {
+                //    String tmp = "";
+                //    for (String ls : locusStrings) {
+                //        tmp += ls;
+                //    }
+                //    log.debug("Found locus string in description: " + probeId + " -> " + tmp);
+                //
+                //}
 
                 for (String ls : locusStrings) {
                     ls = ls.trim();
@@ -472,21 +459,12 @@ public class GCTDatasetParser {
         if (chr == null) {
             Locus locus = getLocus(probeId);
             if ((locus != null) && locus.isValid()) {
-
-                if (log.isDebugEnabled()) {
-                    log.debug("Probe is a locus: " + probeId);
-                }
-
                 addRow(probeId, locus, values);
                 return;
             }
         }
 
 
-        // and to loop through the list
-        if (log.isDebugEnabled()) {
-            log.debug("Searching probeToGene map");
-        }
         String[] loci = ProbeToGeneMap.getInstance().getGenesForProbe(probeId);
 
         if (loci != null) {
@@ -494,11 +472,6 @@ public class GCTDatasetParser {
                 Locus locus = getLocus(locusString);
                 //Feature gene = FeatureDB.getFeature(locusString);
 
-                if (log.isDebugEnabled()) {
-                    log.debug(locusString + " -> " + locus);
-                }
-
-
                 if (locus != null) {
                     addRow(probeId, locus, values);
                 }
@@ -508,15 +481,24 @@ public class GCTDatasetParser {
 
     private void addRow(String probeId, Locus locus, float[] values) {
 
-        if (log.isDebugEnabled()) {
-            log.debug("Adding row for probe " + probeId + " to chromosome " + locus.getChr());
-        }
+        //if (log.isDebugEnabled()) {
+        //    log.debug("Adding row for probe " + probeId + " to chromosome " + locus.getChr());
+        //}
 
         List<Row> rows = rowMap.get(locus.getChr());
         if (rows == null) {
             rows = new ArrayList();
             rowMap.put(locus.getChr(), rows);
         }
+
+        String chr = locus.getChr();
+        int length = locus.getEnd() - locus.getStart();
+        if (longestProbeMap.containsKey(chr)) {
+            longestProbeMap.put(chr, Math.max(longestProbeMap.get(chr), length));
+        } else {
+            longestProbeMap.put(chr, length);
+        }
+
         rows.add(new Row(probeId, locus.getChr(), locus.getStart(), locus.getEnd(), values));
 
     }
@@ -546,12 +528,7 @@ public class GCTDatasetParser {
 
     }
 
-    /**
-     * Method description
-     *
-     * @param chr
-     * @return
-     */
+
     public int[] getStartLocations(String chr) {
 
         List<Row> rows = rowMap.get(chr);
@@ -562,12 +539,7 @@ public class GCTDatasetParser {
         return startLocations;
     }
 
-    /**
-     * Method description
-     *
-     * @param chr
-     * @return
-     */
+
     public int[] getEndLocations(String chr) {
 
         List<Row> rows = rowMap.get(chr);
@@ -578,13 +550,7 @@ public class GCTDatasetParser {
         return endLocations;
     }
 
-    /**
-     * Method description
-     *
-     * @param heading
-     * @param chr
-     * @return
-     */
+    
     public float[] getData(String heading, String chr) {
 
         int columnIndex = this.headingIndexMap.get(heading);
@@ -647,7 +613,7 @@ public class GCTDatasetParser {
                 if (f == null) {
                     return null;
                 } else {
-                    return new Locus(f.getChromosome(), f.getStart(), f.getEnd());
+                    return new Locus(f.getChr(), f.getStart(), f.getEnd());
                 }
             }
 
@@ -655,7 +621,7 @@ public class GCTDatasetParser {
             else if (geneManager != null) {
                 Feature gene = FeatureDB.getFeature(locusString);
                 if (gene != null) {
-                    return new Locus(gene.getChromosome(), gene.getStart(), gene.getEnd());
+                    return new Locus(gene.getChr(), gene.getStart(), gene.getEnd());
                 }
             }
         }
diff --git a/src/org/broad/igv/data/GenomeSummaryData.java b/src/org/broad/igv/data/GenomeSummaryData.java
index ff022d7..b211f3e 100644
--- a/src/org/broad/igv/data/GenomeSummaryData.java
+++ b/src/org/broad/igv/data/GenomeSummaryData.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,6 +22,9 @@
  */
 package org.broad.igv.data;
 
+import org.broad.igv.util.collections.FloatArrayList;
+import org.broad.igv.util.collections.IntArrayList;
+import org.apache.log4j.Logger;
 import org.broad.igv.feature.Genome;
 import org.broad.igv.tools.Accumulator;
 import org.broad.igv.track.WindowFunction;
@@ -36,13 +39,15 @@ import java.util.Map;
  */
 public class GenomeSummaryData {
 
+    private static Logger log = Logger.getLogger(GenomeSummaryData.class);
+
     // Genome coordinates are in KB
-    long locationUnit = 1000;
+    private static final double locationUnit = 1000.0;
 
     /**
      * Number of virtual pixels
      */
-    int nPixels = 10000;
+    int nPixels = 1000;
 
     Genome genome;
 
@@ -66,7 +71,7 @@ public class GenomeSummaryData {
     public GenomeSummaryData(Genome genome, String[] samples) {
         this.genome = genome;
         this.samples = samples;
-        scale = (genome.getLength() / 1000.0) / nPixels;
+        scale = (genome.getLength() / locationUnit) / nPixels;
 
         List<String> chrNames = genome.getChromosomeNames();
         locationMap = new HashMap();
@@ -83,8 +88,9 @@ public class GenomeSummaryData {
 
     public void addData(String chr, int[] locs, Map<String, float[]> sampleData) {
 
-        if(!locationMap.containsKey(chr)) {
-            return;
+        IntArrayList locations = locationMap.get(chr);
+        if (locations == null) {
+            log.info("Skipping data for: " + chr);
         }
 
         int lastPixel = -1;
@@ -96,14 +102,14 @@ public class GenomeSummaryData {
             int pixel = (int) (genomeLocation / scale);
             if (i > 0 && pixel != lastPixel) {
                 nDataPts++;
-                locationMap.get(chr).add(genomeLocation);
+
+                locations.add(genomeLocation);
                 for (String s : dataMap.get(chr).keySet()) {
                     Accumulator dp = dataPoints.get(s);
                     dp.finish();
                     dataMap.get(chr).get(s).add(dp.getValue(WindowFunction.mean));
                 }
                 dataPoints.clear();
-
             }
 
             for (String s : samples) {
diff --git a/src/org/broad/igv/data/HDFDataManager.java b/src/org/broad/igv/data/HDFDataManager.java
index 790dbcc..609032c 100644
--- a/src/org/broad/igv/data/HDFDataManager.java
+++ b/src/org/broad/igv/data/HDFDataManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -28,24 +28,22 @@ package org.broad.igv.data;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
-import org.broad.igv.feature.*;
+import org.broad.igv.feature.Feature;
+import org.broad.igv.feature.FeatureUtils;
+import org.broad.igv.feature.GeneManager;
+import org.broad.igv.feature.LocusScore;
 import org.broad.igv.h5.DataAccessException;
 import org.broad.igv.h5.HDF5Reader;
 import org.broad.igv.h5.HDF5ReaderFactory;
 import org.broad.igv.h5.ObjectNotFoundException;
-import org.broad.igv.preprocess.old.FeatureBin;
-import org.broad.igv.preprocess.old.FeatureBinCalculator;
 import org.broad.igv.renderer.RendererFactory;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.track.TrackProperties;
 import org.broad.igv.track.TrackType;
 import org.broad.igv.track.WindowFunction;
-import org.broad.igv.ui.IGVModel;
 import org.broad.igv.util.*;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * Manages data from a single HDF5 file.
@@ -69,7 +67,7 @@ public class HDFDataManager {
     /**
      * Cache of zoom level parameters.  Key is the zoom level
      */
-    private LRUCache<String, ZoomParameters> zoomParameterCache = new LRUCache(1000);
+    private LRUCache<String, ZoomParameters> zoomParameterCache = new LRUCache(this, 100);
     /**
      * Cache of rawMeansPath arrays -- shared across all instances.  Used for location arrays
      */
@@ -167,17 +165,19 @@ public class HDFDataManager {
                 datasetId = dataReader.openDataset(dsName);
 
                 // A hack for ChIP data.
-                String[] names = dataReader.readAllStrings(datasetId);
+                List<String> names = dataReader.readAllStrings(datasetId);
+                trackNames = new String[names.size()];
                 if (this.getTrackType() == TrackType.CHIP) {
-                    trackNames = new String[names.length];
                     String strip = ".aligned";
-                    for (int i = 0; i < names.length; i++) {
-                        String nm = names[i];
+                    for (int i = 0; i < names.size(); i++) {
+                        String nm = names.get(i);
                         trackNames[i] = nm.endsWith(strip)
                                 ? nm.substring(0, nm.length() - strip.length()) : nm;
                     }
                 } else {
-                    trackNames = names;
+                    for (int i = 0; i < names.size(); i++) {
+                        trackNames[i] = names.get(i);
+                    }
                 }
 
             } finally {
@@ -667,7 +667,7 @@ public class HDFDataManager {
     private synchronized int getLongestFeature(String chr) {
 
         if (!longestFeatureCache.containsKey(chr)) {
-            String genomeId = IGVModel.getInstance().getViewContext().getGenomeId();
+            String genomeId = ViewContext.getInstance().getGenomeId();
             // Put a limit on the "longest feature" to prevent outliers from dictating
             // huge data loads.  This is neccessary due to a flaw in the indexing scheme.
             int maxLongestFeature = GeneManager.getGeneManager(genomeId).getLongestGeneLength(chr) + 100;
@@ -706,7 +706,7 @@ public class HDFDataManager {
 
         // The binWidth is the width in base pairs of a single screen pixel
         // TODO -- binWidth should be passed in
-        double binWidth = IGVModel.getInstance().getViewContext().getScale();
+        double binWidth = ViewContext.getInstance().getScale();
 
         // Get the raw data as a list of tiles.
         List<DataTile2D> tiles = getRawData(chr, startLocation, endLocation);
@@ -815,7 +815,7 @@ public class HDFDataManager {
 
     private static List<FeatureBin> computeBins(int startLocation, int endLocation,
                                                 List<LocusScore> features) {
-        double binSize = IGVModel.getInstance().getViewContext().getScale();
+        double binSize = ViewContext.getInstance().getScale();
         int nBins = (int) Math.ceil((endLocation - startLocation) / binSize);
         double correctedBinSize = ((double) (endLocation - startLocation)) / nBins;
         return (new FeatureBinCalculator()).computeFeatureBins(features, nBins, correctedBinSize,
diff --git a/src/org/broad/igv/data/HDFDataManagerFactory.java b/src/org/broad/igv/data/HDFDataManagerFactory.java
index 2fb7c6f..95dce38 100644
--- a/src/org/broad/igv/data/HDFDataManagerFactory.java
+++ b/src/org/broad/igv/data/HDFDataManagerFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/data/HDFDataSource.java b/src/org/broad/igv/data/HDFDataSource.java
index bd49f9e..334a659 100644
--- a/src/org/broad/igv/data/HDFDataSource.java
+++ b/src/org/broad/igv/data/HDFDataSource.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,6 +22,7 @@
  */
 package org.broad.igv.data;
 
+import org.broad.igv.Globals;
 import org.broad.igv.feature.LocusScore;
 import org.broad.igv.track.TrackType;
 import org.broad.igv.track.WindowFunction;
@@ -74,12 +75,12 @@ public class HDFDataSource implements DataSource {
         return dataManager.getChrLength(chr);
     }
 
-    public double getDataMax(String chr) {
-        return dataManager.getDataMax(trackNumber, chr);
+    public double getDataMax() {
+        return dataManager.getDataMax(trackNumber, Globals.CHR_ALL);
     }
 
-    public double getDataMin(String chr) {
-        return dataManager.getDataMin(trackNumber, chr);
+    public double getDataMin() {
+        return dataManager.getDataMin(trackNumber, Globals.CHR_ALL);
     }
 
     /**
diff --git a/src/org/broad/igv/data/IGVDataset.java b/src/org/broad/igv/data/IGVDataset.java
index 3d98b47..cba01e8 100644
--- a/src/org/broad/igv/data/IGVDataset.java
+++ b/src/org/broad/igv/data/IGVDataset.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -68,6 +68,8 @@ public class IGVDataset implements Dataset {
 
     TrackProperties trackProperties = new TrackProperties();
 
+    private Map<String, Integer> longestFeatureMap;
+
     public IGVDataset(String genomeId, ResourceLocator locator) {
         this.genomeId = genomeId;
 
@@ -145,7 +147,8 @@ public class IGVDataset implements Dataset {
     }
 
     public String[] getFeatureNames(String chr) {
-        return null;
+        ChromosomeData cd = getChromosomeData(chr);
+        return cd == null ? null : cd.getProbes();
     }
 
     public int getWindowSpan() {
@@ -215,4 +218,14 @@ public class IGVDataset implements Dataset {
     public TrackProperties getTrackProperties() {
         return trackProperties;
     }
+
+
+    public Integer getLongestFeature(String chr) {
+        return longestFeatureMap == null ? 1000 :
+                longestFeatureMap.containsKey(chr) ? longestFeatureMap.get(chr) : 1;
+    }
+
+    public void setLongestFeatureMap(Map<String, Integer> longestFeatureMap) {
+        this.longestFeatureMap = longestFeatureMap;
+    }
 }
diff --git a/src/org/broad/igv/data/IGVDatasetParser.java b/src/org/broad/igv/data/IGVDatasetParser.java
index 7f537ff..5a0f146 100644
--- a/src/org/broad/igv/data/IGVDatasetParser.java
+++ b/src/org/broad/igv/data/IGVDatasetParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -19,8 +19,10 @@ package org.broad.igv.data;
 
 //~--- non-JDK imports --------------------------------------------------------
 
-import cern.colt.list.ObjectArrayList;
+import org.broad.igv.util.collections.FloatArrayList;
+import org.broad.igv.util.collections.IntArrayList;
 import org.apache.log4j.Logger;
+import org.broad.igv.Globals;
 import org.broad.igv.event.StatusChangeEvent;
 import org.broad.igv.exceptions.ParserException;
 import org.broad.igv.feature.Genome;
@@ -28,7 +30,9 @@ import org.broad.igv.feature.GenomeManager;
 import org.broad.igv.listener.StatusListener;
 import org.broad.igv.track.TrackType;
 import org.broad.igv.track.WindowFunction;
+import org.broad.igv.util.SeekableStream;
 import org.broad.igv.util.*;
+import org.broad.igv.util.SeekableStreamFactory;
 
 import java.awt.*;
 import java.io.FileInputStream;
@@ -55,15 +59,13 @@ public class IGVDatasetParser {
     private int startColumn;
     private int endColumn;
     private int firstDataColumn;
+    private int probeColumn;
     private boolean hasEndLocations;
     private boolean hasCalls;
     private Genome genome;
-    public static ArrayList<StatusListener> listeners = null;
 
-    enum FileType {
 
-        IGV, SNP, CN, XCN
-    }
+    public static ArrayList<StatusListener> listeners = null;
 
     /**
      * Constructs ...
@@ -82,23 +84,22 @@ public class IGVDatasetParser {
                 ? dataResourceLocator.getPath().substring(0,
                 dataResourceLocator.getPath().length() - 4) : dataResourceLocator.getPath()).toLowerCase();
 
-
         if (tmp.endsWith(".igv")) {
             chrColumn = 0;
             startColumn = 1;
             endColumn = 2;
+            probeColumn = 3;
             firstDataColumn = 4;
             hasEndLocations = true;
             hasCalls = false;
-
         } else if (tmp.endsWith(".xcn") || tmp.endsWith("cn") || tmp.endsWith(".snp") || tmp.endsWith(".loh")) {
+            probeColumn = 0;
             chrColumn = 1;
             startColumn = 2;
             endColumn = -1;
             firstDataColumn = 3;
             hasEndLocations = false;
             hasCalls = tmp.endsWith(".xcn") || tmp.endsWith(".snp");
-
         } else {
             throw new ParserException("Unknown file type: ", 0);
         }
@@ -106,7 +107,8 @@ public class IGVDatasetParser {
 
     /*
      */
-    public static boolean parsableMAGE_TAB(ResourceLocator file) {
+
+    public static boolean parsableMAGE_TAB(ResourceLocator file) throws IOException {
         org.broad.igv.util.AsciiLineReader reader = null;
         try {
             reader = ParsingUtils.openAsciiReader(file);
@@ -129,9 +131,6 @@ public class IGVDatasetParser {
                 }
                 return false;
             }
-        } catch (IOException e) {
-            e.printStackTrace();
-            return false;
         } finally {
             if (reader != null) {
                 reader.close();
@@ -150,6 +149,7 @@ public class IGVDatasetParser {
     public List<ChromosomeSummary> scan(IGVDataset dataset) {
 
         int estLineCount = ParsingUtils.estimateLineCount(dataResourceLocator.getPath());
+        Map<String, Integer> longestFeatureMap = new HashMap();
 
         float dataMin = 0;
         float dataMax = 0;
@@ -246,13 +246,29 @@ public class IGVDatasetParser {
 
                     } catch (NumberFormatException numberFormatException) {
                         log.error("Column " + tokens[startColumn] + " is not a number");
-
-                        throw new ParserException("Column " + (startColumn + 1) + " must contain a numeric value." + " Found: " + tokens[startColumn],
+                        throw new ParserException("Column " + (startColumn + 1) +
+                                " must contain a numeric value." + " Found: " + tokens[startColumn],
                                 reader.getCurrentLineNumber(), nextLine);
                     }
 
+                    int length = 1;
+                    if (hasEndLocations) {
+                        try {
+                            length = Integer.parseInt(tokens[endColumn].trim()) - location + 1;
+
+                        } catch (NumberFormatException numberFormatException) {
+                            log.error("Column " + tokens[endColumn] + " is not a number");
+                            throw new ParserException("Column " + (endColumn + 1) +
+                                    " must contain a numeric value." + " Found: " + tokens[endColumn],
+                                    reader.getCurrentLineNumber(), nextLine);
+                        }
+                    }
+
+                    updateLongestFeature(longestFeatureMap, thisChr, length);
+
                     if (wgData.locations.size() > 0 && wgData.locations.get(wgData.locations.size() - 1) > location) {
-                        throw new ParserException("File is not sorted.", reader.getCurrentLineNumber());
+                        throw new ParserException("File is not sorted, .igv and .cn files must be sorted by start position." +
+                                " Use igvtools (File > Run igvtools..) to sort the file.", reader.getCurrentLineNumber());
                     }
 
 
@@ -286,6 +302,9 @@ public class IGVDatasetParser {
                 }
                 chrRowCount++;
             }
+
+            dataset.setLongestFeatureMap(longestFeatureMap);
+
         }
         catch (ParserException pe) {
             throw pe;
@@ -322,10 +341,20 @@ public class IGVDatasetParser {
         return chrSummaries;
     }
 
+    private void updateLongestFeature(Map<String, Integer> longestFeatureMap, String thisChr, int length) {
+        if (longestFeatureMap.containsKey(thisChr)) {
+            longestFeatureMap.put(thisChr, Math.max(longestFeatureMap.get(thisChr), length));
+        } else {
+            longestFeatureMap.put(thisChr, length);
+        }
+    }
+
     private float readFloat(String token) {
         float copyNo = Float.NaN;
         try {
-            copyNo = Float.parseFloat(token);
+            if (token != null) {
+                copyNo = Float.parseFloat(token);
+            }
 
         } catch (NumberFormatException e) {
         }
@@ -349,17 +378,15 @@ public class IGVDatasetParser {
             // Get an estimate of the number of snps (rows).  THIS IS ONLY AN ESTIMATE
             int nRowsEst = chrSummary.getNDataPts();
 
-
-            String path = dataResourceLocator.getPath();
-            SeekableStream is = SeekableStreamFactory.getStreamFor(dataResourceLocator);
+            SeekableStream is = SeekableStreamFactory.getStreamFor(dataResourceLocator.getPath());
             is.seek(chrSummary.getStartPosition());
             AsciiLineReader reader = new AsciiLineReader(is);
 
-            String nextLine = reader.readLine();
 
             // Create containers to hold data
             IntArrayList startLocations = new IntArrayList(nRowsEst);
             IntArrayList endLocations = (hasEndLocations ? new IntArrayList(nRowsEst) : null);
+            List<String> probes = new ArrayList(nRowsEst);
 
             Map<String, FloatArrayList> dataMap = new HashMap();
             for (String h : columnHeaders) {
@@ -369,6 +396,7 @@ public class IGVDatasetParser {
             // Begin loop through rows
             String chromosome = chrSummary.getName();
             boolean chromosomeStarted = false;
+            String nextLine = reader.readLine();
             while ((nextLine != null) && (nextLine.trim().length() > 0)) {
 
                 try {
@@ -380,6 +408,10 @@ public class IGVDatasetParser {
 
                         // chromosomeData.setMarkerId(nRows, tokens[0]);
 
+                        // The probe.  A new string is created to prevent holding on to the entire row through a substring reference
+                        String probe = new String(tokens[probeColumn]);
+                        probes.add(probe);
+
                         int start = Integer.parseInt(tokens[startColumn].trim());
                         if (hasEndLocations) {
                             endLocations.add(Integer.parseInt(tokens[endColumn].trim()));
@@ -408,8 +440,9 @@ public class IGVDatasetParser {
                 nextLine = reader.readLine();
             }
 
-// Loop complete
+            // Loop complete
             ChromosomeData cd = new ChromosomeData(chrSummary.getName());
+            cd.setProbes(probes.toArray(new String[]{}));
             cd.setStartLocations(startLocations.toArray());
             if (hasEndLocations) {
                 cd.setEndLocations(endLocations.toArray());
@@ -464,7 +497,7 @@ public class IGVDatasetParser {
 
     private boolean isCopyNumberFileExt(String filename) {
         String tmp = (filename.endsWith(".txt") || filename.endsWith(".tab") || filename.endsWith(".xls")
-                ? filename.substring(0, filename.length() - 4) : filename);
+                ? filename.substring(0, filename.length() - 4).toLowerCase() : filename).toLowerCase();
         return tmp.endsWith(".cn") || tmp.endsWith(".xcn") || tmp.endsWith(".snp");
     }
 
@@ -495,7 +528,7 @@ public class IGVDatasetParser {
      */
     public String[] getHeadings(String[] tokens, int skipColumns, boolean removeDuplicates) {
 
-        ObjectArrayList headings = new ObjectArrayList();
+        ArrayList headings = new ArrayList();
         String previousHeading = null;
         for (int i = firstDataColumn; i < tokens.length; i += skipColumns) {
             if (removeDuplicates) {
@@ -522,7 +555,7 @@ public class IGVDatasetParser {
      * @throws java.io.IOException
      */
     private void position(InputStream is, long position) throws IOException {
-        // This is ugly, but most streams are filestreams and we want to take advantage of
+        // This is ugly, but most streams are filestreams and we want to take advantage of 
         // the file channel
         if (is instanceof FileInputStream) {
             ((FileInputStream) is).getChannel().position(position);
@@ -535,6 +568,12 @@ public class IGVDatasetParser {
 
     private void updateWholeGenome(String currentChromosome, IGVDataset dataset, String[] headings,
                                    IGVDatasetParser.WholeGenomeData wgData) {
+
+
+        if (!genome.getHomeChromosome().equals(Globals.CHR_ALL)) {
+            return;
+        }
+
         // Update whole genome data
         int[] locations = wgData.locations.toArray();
         if (locations.length > 0) {
diff --git a/src/org/broad/igv/data/IntArrayList.java b/src/org/broad/igv/data/IntArrayList.java
deleted file mode 100644
index eca0138..0000000
--- a/src/org/broad/igv/data/IntArrayList.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-package org.broad.igv.data;
-
-/**
- * @author jrobinso
- */
-public class IntArrayList {
-
-    static final int maxGrowIncrement = Integer.MAX_VALUE / 10;
-
-    private int sz = 0;
-    private int[] values;
-
-    public IntArrayList(int maxSize) {
-        values = new int[maxSize];
-    }
-
-    public int size() {
-        return sz;
-    }
-
-    public void clear() {
-        sz = 0;
-    }
-
-    public void add(int v) {
-        if (sz >= values.length) {
-            grow();
-        }
-        values[sz] = v;
-        sz++;
-    }
-
-    public void addAll(int[] array) {
-        if (values.length - sz < array.length) {
-            grow(array.length);
-        }
-        System.arraycopy(array, 0, values, sz, array.length);
-        sz += array.length;
-    }
-
-    public int[] toArray() {
-        trim();
-        return values;
-    }
-
-    public int get(int idx) {
-        if (idx >= sz) {
-            throw new IndexOutOfBoundsException("Index out of bounds.  Size = " + sz + " Index = " + idx);
-        } else {
-            return values[idx];
-        }
-    }
-
-    private void grow() {
-        grow((int) (Math.max(1000, 0.2 * values.length)));
-    }
-
-    private void grow(int increment) {
-        if (values.length >= Integer.MAX_VALUE) {
-            throw new RuntimeException("Maximum array size exceeded");
-        }
-        int newSize = Math.min(Integer.MAX_VALUE, values.length + increment);
-        resize(newSize);
-
-    }
-
-    private void resize(int newSize) {
-        int[] tmp = new int[newSize];
-        System.arraycopy(values, 0, tmp, 0, Math.min(tmp.length, values.length));
-        values = tmp;
-    }
-
-    private void trim() {
-        resize(sz);
-    }
-
-    public void reorder(int[] indeces) {
-        if (indeces.length != sz) {
-            throw new IllegalArgumentException("Index array length not equal to size");
-        }
-        int[] reorderedValues = new int[sz];
-        for (int i = 0; i < sz; i++) {
-            reorderedValues[i] = values[indeces[i]];
-        }
-        values = reorderedValues;
-    }
-
-}
diff --git a/src/org/broad/igv/data/LEDTest.java b/src/org/broad/igv/data/LEDTest.java
deleted file mode 100644
index 1fc5352..0000000
--- a/src/org/broad/igv/data/LEDTest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.data;
-
-import com.mindprod.ledatastream.LEDataInputStream;
-import com.mindprod.ledatastream.LEDataOutputStream;
-
-import java.io.*;
-
-/**
- * @author jrobinso
- */
-public class LEDTest {
-
-    static int nPts = 5000000;
-
-    public static void main(String[] args) {
-        File ledFile = new File("ledtest.bin");
-        writeLED(ledFile);
-        writeBin(ledFile);
-    }
-
-    static void writeLED(File file) {
-
-        OutputStream os = null;
-        InputStream is = null;
-
-        try {
-            long t0 = System.currentTimeMillis();
-            os = new BufferedOutputStream(new FileOutputStream(file));
-            LEDataOutputStream ledOS = new LEDataOutputStream(os);
-
-            for (int i = 0; i < nPts; i++) {
-                ledOS.writeDouble(i * 1.0);
-            }
-            System.out.println("LE: wrote " + nPts + " doubles in " + (System.currentTimeMillis() - t0));
-            os.close();
-
-            t0 = System.currentTimeMillis();
-            is = new BufferedInputStream(new FileInputStream(file));
-            LEDataInputStream ledIS = new LEDataInputStream(is);
-            for (int i = 0; i < nPts; i++) {
-                double t = ledIS.readDouble();
-                if (i * 1.0 != t) {
-                    System.out.println("Error");
-                }
-            }
-            System.out.println("LE: read " + nPts + " doubles in " + (System.currentTimeMillis() - t0));
-            is.close();
-
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-
-    static void writeBin(File file) {
-
-        OutputStream os = null;
-        InputStream is = null;
-
-        try {
-            long t0 = System.currentTimeMillis();
-            os = new BufferedOutputStream(new FileOutputStream(file));
-            DataOutputStream ledOS = new DataOutputStream(os);
-
-            for (int i = 0; i < nPts; i++) {
-                ledOS.writeDouble(i * 1.0);
-            }
-            System.out.println("BE: wrote " + nPts + " doubles in " + (System.currentTimeMillis() - t0));
-            os.close();
-
-            t0 = System.currentTimeMillis();
-            is = new BufferedInputStream(new FileInputStream(file));
-            DataInputStream ledIS = new DataInputStream(is);
-            for (int i = 0; i < nPts; i++) {
-                double t = ledIS.readDouble();
-                if (i * 1.0 != t) {
-                    System.out.println("Error");
-                }
-            }
-            System.out.println("BE: read " + nPts + " doubles in " + (System.currentTimeMillis() - t0));
-            is.close();
-
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-}
diff --git a/src/org/broad/igv/data/LocusScoreUtils.java b/src/org/broad/igv/data/LocusScoreUtils.java
new file mode 100644
index 0000000..34313ec
--- /dev/null
+++ b/src/org/broad/igv/data/LocusScoreUtils.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.data;
+
+import org.broad.igv.feature.LocusScore;
+
+import java.util.*;
+
+
+/**
+ * @author jrobinso
+ */
+public class LocusScoreUtils {
+
+
+    /**
+     * Segregate a list of possibly overlapping features into a list of
+     * non-overlapping lists of features.
+     */
+    public static List<List<LocusScore>> segreateFeatures(List<LocusScore> features, double scale) {
+
+        // Create a list to hold the lists of non-overlapping features
+        List<List<LocusScore>> segmentedLists = new ArrayList();
+
+        // Make a working copy of the original list.
+        List<LocusScore> workingList = new LinkedList(features);
+        sortFeatureList(workingList);
+
+        // Loop until all features have been allocated to non-overlapping lists
+        while (workingList.size() > 0) {
+
+            List<LocusScore> nonOverlappingFeatures = new LinkedList();
+            List<LocusScore> overlappingFeatures = new LinkedList();
+
+            // Prime the loop with the first feature, it can't overlap itself
+            LocusScore f1 = workingList.remove(0);
+            nonOverlappingFeatures.add(f1);
+            while (workingList.size() > 0) {
+                LocusScore f2 = workingList.remove(0);
+                int scaledStart = (int) (f2.getStart() / scale);
+                int scaledEnd = (int) (f1.getEnd() / scale);
+                if (scaledStart > scaledEnd) {
+                    nonOverlappingFeatures.add(f2);
+                    f1 = f2;
+                } else {
+                    overlappingFeatures.add(f2);
+                }
+            }
+
+            // Add the list of non-overlapping features and start again with whats left
+            segmentedLists.add(nonOverlappingFeatures);
+            workingList = overlappingFeatures;
+        }
+        return segmentedLists;
+    }
+
+    /**
+     * Sort the feature list by ascending start value
+     */
+    public static void sortFeatureList(List<? extends LocusScore> features) {
+
+        Collections.sort(features, new Comparator() {
+
+            public int compare(Object o1, Object o2) {
+                LocusScore f1 = (LocusScore) o1;
+                LocusScore f2 = (LocusScore) o2;
+                return (int) (f1.getStart() - f2.getStart());
+            }
+        });
+    }
+
+    public static LocusScore getFeatureAt(double position, double minWidth,
+                                          List<? extends LocusScore> features) {
+
+        int startIdx = 0;
+        int endIdx = features.size();
+
+        while (startIdx != endIdx) {
+            int idx = (startIdx + endIdx) / 2;
+
+            LocusScore feature = features.get(idx);
+
+            // Correct for zero vs 1 based coordinates.
+            // TODO -- document this.
+            double effectiveStart = feature.getStart() + 1;
+            if (position >= effectiveStart) {
+
+                double effectiveWidth = Math.max(minWidth, feature.getEnd() - feature.getStart());
+
+                if (position <= effectiveStart + effectiveWidth) {
+                    return features.get(idx);
+                } else {
+                    if (idx == startIdx) {
+                        return null;
+                    } else {
+                        startIdx = idx;
+                    }
+                }
+            } else {
+                endIdx = idx;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the index of the feature just to the right of the given position.
+     * If there is no feature to the right return -1;
+     *
+     * @param position
+     * @param features
+     * @return
+     */
+    public static LocusScore getFeatureAfter(double position, List<? extends LocusScore> features) {
+
+        if (features.size() == 0 ||
+                features.get(features.size() - 1).getStart() <= position) {
+            return null;
+        }
+
+        int startIdx = 0;
+        int endIdx = features.size();
+
+        // Narrow the list to ~ 10
+        while (startIdx != endIdx) {
+            int idx = (startIdx + endIdx) / 2;
+            double distance = features.get(idx).getStart() - position;
+            if (distance <= 0) {
+                startIdx = idx;
+            } else {
+                endIdx = idx;
+            }
+            if (endIdx - startIdx < 10) {
+                break;
+            }
+        }
+
+        // Now find feature
+        for (int idx = startIdx; idx < features.size(); idx++) {
+            if (features.get(idx).getStart() > position) {
+                return features.get(idx);
+            }
+        }
+
+        return null;
+
+    }
+
+    public static LocusScore getFeatureBefore(double position, List<? extends LocusScore> features) {
+
+        if (features.size() == 0 ||
+                features.get(features.size() - 1).getStart() <= position) {
+            return null;
+        }
+
+        int startIdx = 0;
+        int endIdx = features.size() - 1;
+
+        while (startIdx != endIdx) {
+            int idx = (startIdx + endIdx) / 2;
+            double distance = features.get(idx).getStart() - position;
+            if (distance <= 0) {
+                startIdx = idx;
+            } else {
+                endIdx = idx;
+            }
+            if (endIdx - startIdx < 10) {
+                break;
+            }
+        }
+
+        if (features.get(endIdx).getStart() >= position) {
+            for (int idx = endIdx; idx >= 0; idx--) {
+                if (features.get(idx).getStart() < position) {
+                    return features.get(idx);
+                }
+            }
+        } else {
+            for (int idx = endIdx + 1; idx < features.size(); idx++) {
+                if (features.get(idx).getStart() >= position) {
+                    return features.get(idx - 1);
+                }
+
+            }
+        }
+        return null;
+
+
+    }
+}
diff --git a/src/org/broad/igv/data/NamedSummaryScore.java b/src/org/broad/igv/data/NamedSummaryScore.java
index 165a085..0e614a4 100644
--- a/src/org/broad/igv/data/NamedSummaryScore.java
+++ b/src/org/broad/igv/data/NamedSummaryScore.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/data/ProbeSet.java b/src/org/broad/igv/data/ProbeSet.java
index 5b8877a..9d9bfa4 100644
--- a/src/org/broad/igv/data/ProbeSet.java
+++ b/src/org/broad/igv/data/ProbeSet.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/data/ProcessingUtils.java b/src/org/broad/igv/data/ProcessingUtils.java
index 1c4843e..2e76bc5 100644
--- a/src/org/broad/igv/data/ProcessingUtils.java
+++ b/src/org/broad/igv/data/ProcessingUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -21,19 +21,12 @@
  */
 package org.broad.igv.data;
 
-//~--- non-JDK imports --------------------------------------------------------
+import org.broad.igv.util.collections.IntArrayList;
 
-import cern.colt.list.DoubleArrayList;
-import cern.jet.stat.quantile.DoubleQuantileFinder;
-import cern.jet.stat.quantile.QuantileFinderFactory;
+import org.apache.commons.math.stat.StatUtils;
 import org.broad.igv.feature.LocusScore;
-import org.broad.igv.preprocess.old.BinaryReader;
 import org.broad.igv.track.WindowFunction;
 
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
 import java.util.*;
 
 /**
@@ -88,21 +81,12 @@ public class ProcessingUtils {
     }
 
     private static float computeQuantile(float[] data, double quantile) {
-        DoubleArrayList al = new DoubleArrayList(2);
-        al.add(quantile);
-        DoubleQuantileFinder qf = QuantileFinderFactory.newDoubleQuantileFinder(true, data.length,
-                0.001, 0.001, al.size(), null);
-        for (int i = 0; i < data.length; i++) {
-            if (isValidData(data[i])) {
-                qf.add(data[i]);
-            }
-        }
-        if (qf.size() > 0) {
 
-            return (float) qf.quantileElements(al).get(0);
-        } else {
-            return Float.NaN;
+        double[] dData = new double[data.length];
+        for (int i = 0; i < data.length; i++) {
+            dData[i] = data[i];
         }
+        return (float) StatUtils.percentile(dData, quantile);
     }
 
     private static float computeMin(float[] data) {
@@ -137,13 +121,7 @@ public class ProcessingUtils {
         return (nPts == 0 ? Float.NaN : sum / nPts);
     }
 
-    /**
-     * Method description
-     *
-     * @param data
-     * @param function
-     * @return
-     */
+
     public static float computeStat(float[] data, WindowFunction function) {
 
         if (nullDataCheck(data)) {
@@ -153,17 +131,19 @@ public class ProcessingUtils {
                 case mean:
                     return computeMean(data);
                 case median:
-                    return computeQuantile(data, 0.5);
+                    return computeQuantile(data, 50);
                 case min:
                     return computeMin(data);
                 case max:
                     return computeMax(data);
+                case percentile2:
+                    return computeQuantile(data, 2);
                 case percentile10:
-                    return computeQuantile(data, 0.1);
+                    return computeQuantile(data, 10);
                 case percentile90:
-                    return computeQuantile(data, 0.9);
+                    return computeQuantile(data, 90);
                 case percentile98:
-                    return computeQuantile(data, 0.98);
+                    return computeQuantile(data, 98);
                 case count:
                     return data.length;
 
@@ -173,238 +153,6 @@ public class ProcessingUtils {
 
     }
 
-    /**
-     * Compute some statistics for the input data
-     * TODO move to a utility class
-     * TODO take statis desired as input
-     *
-     * @param data
-     * @return
-     */
-    public static DataStatistics computeStats(float[] data) {
-
-        if (nullDataCheck(data)) {
-            return DataStatistics.nullDataStat;
-        } else {
-
-            DataStatistics stats = new DataStatistics();
-
-            // Check for array of NaNs
-            DoubleArrayList al = new DoubleArrayList(2);
-            al.add(0.1);
-            al.add(0.5);
-            al.add(0.9);
-            al.add(0.98);
-            DoubleQuantileFinder qf = QuantileFinderFactory.newDoubleQuantileFinder(true,
-                    data.length, 0.001, 0.001, al.size(), null);
-
-
-            double minStat = Double.MAX_VALUE;
-            double maxStat = -Double.MAX_VALUE;
-            double meanStat = 0;
-            for (int i = 0; i < data.length; i++) {
-                if (isValidData(data[i])) {
-                    qf.add(data[i]);
-                    minStat = Math.min(minStat, data[i]);
-                    maxStat = Math.max(maxStat, data[i]);
-                    meanStat += data[i];
-                }
-            }
-
-            if (qf.size() > 0) {
-
-                meanStat /= qf.size();
-                double err2 = 0;
-                for (int i = 0; i < data.length; i++) {
-                    if (isValidData(data[i])) {
-                        err2 += (data[i] - meanStat) * (data[i] - meanStat);
-                    }
-                }
-                double stdDev = Math.sqrt(err2 / qf.size());
-
-                DoubleArrayList quantiles2 = qf.quantileElements(al);
-                stats.setMin(minStat);
-                stats.setMax(maxStat);
-                stats.setMean(meanStat);
-                stats.setPercentile10(quantiles2.get(0));
-                stats.setMedian(quantiles2.get(1));
-                stats.setPercentile90(quantiles2.get(2));
-                stats.setPercentile98(quantiles2.get(3));
-                stats.setStdDev(stdDev);
-            }
-
-            return stats;
-
-        }
-    }
-
-    /**
-     * @param data
-     * @return
-     */
-    public double computeMedian(float[] data) {
-
-        DataStatistics stats = new DataStatistics();
-
-
-        if (data.length > 0) {
-            DoubleArrayList al = new DoubleArrayList();
-            al.add(0.5);
-            DoubleQuantileFinder qf = QuantileFinderFactory.newDoubleQuantileFinder(true,
-                    data.length, 0.001, 0.001, al.size(), null);
-
-
-            for (int i = 0; i < data.length; i++) {
-                if (isValidData(data[i])) {
-                    qf.add(data[i]);
-
-                }
-            }
-
-            if (qf.size() > 0) {
-
-                DoubleArrayList quantiles2 = qf.quantileElements(al);
-                return quantiles2.get(0);
-            }
-        }
-        return 0;
-    }
-
-    private static boolean isValidData(float data) {
-
-        // if (chipFormat) {
-        // return !Float.isNaN(data) && (data >= 0);
-        // } else {
-        return !Float.isNaN(data);
-
-        // }
-    }
-
-    /**
-     * Dump the first N data values from the given file as a java formatted
-     * array.  Used for debugging
-     * and testing.
-     *
-     * @param file
-     * @param type
-     * @param nPoints
-     */
-    public static void dump(File file, Class type, int nPoints) {
-
-        BinaryReader reader = new BinaryReader();
-
-        System.out.print("{");
-        if (type == Long.class) {
-            long[] values = reader.readLongs(file);
-            for (int i = 0; i < nPoints; i++) {
-                System.out.print(values[i]);
-                if (i < nPoints - 1) {
-                    System.out.print(", ");
-                }
-            }
-            System.out.println("}");
-        } else if (type == Float.class) {
-            float[] values = reader.readFloats(file);
-            for (int i = 0; i < nPoints; i++) {
-                System.out.print(values[i]);
-                if (i < nPoints - 1) {
-                    System.out.print(", ");
-                }
-            }
-            System.out.println("}");
-        }
-    }
-
-    /**
-     * Count the number of points between start and end location.  Used
-     * for debugging.
-     * and testing.
-     *
-     * @param file
-     * @param startLocation
-     * @param endLocation
-     */
-    public static void countPoints(File file, long startLocation, long endLocation) {
-
-        BinaryReader reader = new BinaryReader();
-        long[] values = reader.readLongs(file);
-        int count = 0;
-        for (int i = 0; i < values.length; i++) {
-            if (values[i] >= endLocation) {
-                break;
-            }
-            if (values[i] >= startLocation) {
-                count++;
-            }
-        }
-        System.out.println("Count= " + count);
-    }
-
-    /**
-     * Method description
-     *
-     * @param file
-     * @param startLocation
-     * @param endLocation
-     */
-    public static void dumpPoints(File file, long startLocation, long endLocation) {
-
-        PrintWriter pw = null;
-        try {
-            pw = new PrintWriter(new FileWriter("feature_dump_gt.txt"));
-            BinaryReader reader = new BinaryReader();
-            long[] values = reader.readLongs(file);
-            for (int i = 0; i < values.length; i++) {
-                if (values[i] >= endLocation) {
-                    break;
-                }
-                if (values[i] >= startLocation) {
-                    pw.println(values[i]);
-
-                }
-            }
-        } catch (IOException ex) {
-            ex.printStackTrace();
-        } finally {
-            pw.close();
-
-        }
-    }
-
-    /**
-     * Method description
-     *
-     * @param args
-     * @return
-     */
-    public static LinkedHashMap<String, String> parseArgs(String[] args) {
-        LinkedHashMap<String, String> argMap = new LinkedHashMap();
-
-        for (int i = 0; i < args.length; i++) {
-            System.out.println(args[i]);
-
-            String key = args[i];
-
-            if (args[i].startsWith("-")) {
-                if (i < args.length - 1) {
-                    i++;
-
-                    if (args[i].startsWith("-")) {
-                        argMap.put(key, "");
-                        i--;
-                    } else {
-                        argMap.put(key, args[i]);
-                    }
-                } else {
-                    argMap.put(key, "");
-                }
-            } else {
-                argMap.put(key, key);
-            }
-        }
-
-        return argMap;
-    }
 
     static class Interval {
 
@@ -488,17 +236,5 @@ public class ProcessingUtils {
 
     }
 
-    /**
-     * Method description
-     *
-     * @param args
-     */
-    public static void main(String[] args) {
-        long startLocation = 4568550;
-        long endLocation = 4611950;
-        dumpPoints(new File("test/data/es/chrX.snplocation.bin"), startLocation, endLocation);
 
-        // dump(new File("test/data/es/chrX.snplocation.bin"), Long.class, 20);
-        // dump(new File("test/data/es/chrX.ES.K27.bin"), Float.class, 20);
-    }
 }
diff --git a/src/org/broad/igv/data/Segment.java b/src/org/broad/igv/data/Segment.java
deleted file mode 100644
index a92146f..0000000
--- a/src/org/broad/igv/data/Segment.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-package org.broad.igv.data;
-
-import org.broad.igv.feature.LocusScore;
-import org.broad.igv.track.WindowFunction;
-
-/**
- * @author Enter your name here...
- * @version Enter version here..., 09/01/09
- */
-public class Segment implements LocusScore {
-
-    private int extendedStart = -1;
-    private int extendedEnd = -1;
-    private int start;
-    private int end;
-    private float score;
-    private int snpCount;
-
-    public Segment(int start, int end, float score) {
-        this(start, end, score, 0);
-    }
-
-    public Segment(int start, int end, float score, int snpCount) {
-        this.start = start;
-        this.end = end;
-        if (extendedStart < 0) {
-            extendedStart = start;
-        }
-        if (extendedEnd < 0) {
-            extendedEnd = end;
-        }
-        this.score = score;
-        this.snpCount = snpCount;
-    }
-
-
-    //Segment(int start, int end,  float score, int snpCount) {
-    //    this(start, start, end, end, score, snpCount);
-    //}
-    public Segment(int start, int origStart, int end, int origEnd, float score, int snpCount) {
-        this.start = start;
-        this.end = end;
-        this.extendedStart = origStart;
-        this.extendedEnd = origEnd;
-        this.score = score;
-        this.snpCount = snpCount;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public Segment copy() {
-        return new Segment(start, extendedStart, end, extendedEnd, score, snpCount);
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public int getStart() {
-        return start;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public int getEnd() {
-        return end;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public float getScore() {
-        return score;
-    }
-
-    /**
-     * Method description
-     *
-     * @param start
-     */
-    public void setStart(int start) {
-        this.start = start;
-    }
-
-    /**
-     * Method description
-     *
-     * @param end
-     */
-    public void setEnd(int end) {
-        this.end = end;
-    }
-
-    /**
-     * Method description
-     *
-     * @param confidence
-     */
-    public void setConfidence(float confidence) {
-
-        // Ignored for now
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public float getConfidence() {
-        return 1.0f;
-    }
-
-    /**
-     * Method description
-     *
-     * @param position
-     * @param ignored
-     * @return
-     */
-    public String getValueString(double position, WindowFunction ignored) {
-        String valueString = "Copy number: " + getScore();
-        if (snpCount > 0) {
-            valueString += " (" + snpCount + " markers)";
-        }
-        return valueString;
-    }
-
-    /**
-     * Method description
-     *
-     * @param inc
-     */
-    public void incremenetSnpCount(int inc) {
-        snpCount += inc;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public int getSnpCount() {
-        return snpCount;
-    }
-
-    /**
-     * @return the extendedStart
-     */
-    public int getExtendedStart() {
-        return extendedStart;
-    }
-
-    /**
-     * @return the extendedEnd
-     */
-    public int getExtendedEnd() {
-        return extendedEnd;
-    }
-
-    /**
-     * @param extendedStart the extendedStart to set
-     */
-    public void setExtendedStart(int extendedStart) {
-        this.extendedStart = extendedStart;
-    }
-
-    /**
-     * @param extendedEnd the extendedEnd to set
-     */
-    public void setExtendedEnd(int extendedEnd) {
-        this.extendedEnd = extendedEnd;
-    }
-}
diff --git a/src/org/broad/igv/data/SegmentFileParser.java b/src/org/broad/igv/data/SegmentFileParser.java
deleted file mode 100644
index 47f8106..0000000
--- a/src/org/broad/igv/data/SegmentFileParser.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-package org.broad.igv.data;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import org.apache.log4j.Logger;
-import org.broad.igv.exceptions.ParserException;
-import org.broad.igv.util.ParsingUtils;
-import org.broad.igv.track.TrackType;
-import org.broad.igv.util.AsciiLineReader;
-import org.broad.igv.util.ResourceLocator;
-import org.broad.igv.feature.GenomeManager;
-import org.broad.igv.feature.Genome;
-import org.broad.igv.ui.IGVModel;
-
-/**
- * Class description
- *
- * @author Enter your name here...
- * @version Enter version here..., 09/01/09
- */
-public class SegmentFileParser {
-
-    private static Logger log = Logger.getLogger(SegmentFileParser.class);
-
-    int sampleColumn = 0;
-    int chrColumn = 1;
-    int startColumn = 2;
-    int endColumn = 3;
-    int snpCountColumn = 4;    // Default value
-    int dataColumn = 5;        // Default value
-    ResourceLocator locator;
-
-    /**
-     * Constructs ...
-     *
-     * @param locator
-     */
-    public SegmentFileParser(ResourceLocator locator) {
-        this.locator = locator;
-    }
-
-    /**
-     * Method description
-     *
-     * @param dataset
-     */
-    public void loadSegmentTracks(SegmentedAsciiDataSet dataset) {
-        loadSegments(dataset);
-
-    }
-
-    /**
-     * Return a map of trackId -> segment datasource
-     *
-     * @return
-     */
-    private void loadSegments(SegmentedAsciiDataSet dataset) {
-        AsciiLineReader reader = null;
-        String nextLine = null;
-        try {
-            reader = ParsingUtils.openAsciiReader(locator);
-
-            // Parse comments, if any
-            nextLine = reader.readLine();
-            while (nextLine.startsWith("#") || (nextLine.trim().length() == 0)) {
-                if (nextLine.length() > 0) {
-                    parseComment(nextLine, dataset);
-                }
-                nextLine = reader.readLine();
-            }
-
-            // Read column headings
-            String[] headings = nextLine.split("\t");
-
-            // The data value is always the last column
-            dataColumn = headings.length - 1;
-
-            // We assume the snp count is next to last, but not 0-3
-            snpCountColumn = dataColumn - 1;
-            if (snpCountColumn < 4) {
-                snpCountColumn = -1;
-            }
-
-            String[] tokens = new String[headings.length];
-
-            Genome genome = IGVModel.getInstance().getViewContext().getGenome();
-
-            while ((nextLine = reader.readLine()) != null && (nextLine.trim().length() > 0)) {
-
-                int nTokens = ParsingUtils.split(nextLine, tokens, '\t');
-                if (nTokens > 4) {
-                    String chr =  genome.getChromosomeAlias(tokens[chrColumn].trim());
-                    int start;
-                    int end;
-                    try {
-                        start = Integer.parseInt(tokens[startColumn].trim());
-                    }
-                    catch (NumberFormatException numberFormatException) {
-                        throw new ParserException("Column " + (startColumn + 1) + " must contain a numeric value.",
-                                reader.getCurrentLineNumber(), nextLine);
-                    }
-                    try {
-                        end = Integer.parseInt(tokens[endColumn].trim());
-                    }
-                    catch (NumberFormatException numberFormatException) {
-                        throw new ParserException("Column " + (endColumn + 1) + " must contain a numeric value.",
-                                reader.getCurrentLineNumber(), nextLine);
-                    }
-
-                    String trackId = new String(tokens[sampleColumn].trim());
-
-                    int snpCount = 0;
-                    if (snpCountColumn > 0) {
-                        try {
-                            snpCount = Integer.parseInt(tokens[snpCountColumn]);
-                        } catch (NumberFormatException numberFormatException) {
-
-                            // This is an expected condition, nothing needs done.
-                        }
-                    }
-
-
-                    float value = Float.NaN;
-                    try {
-                        value = Float.parseFloat(tokens[dataColumn]);
-
-                    } catch (NumberFormatException numberFormatException) {
-
-                        // This is an expected condition, nothing needs done.
-                    }
-
-                    dataset.addSegment(trackId, chr, start, end, value, snpCount);
-                }
-            }
-
-        }
-        catch (ParserException pe) {
-            throw pe;
-        }
-        catch (Exception e) {
-            if (nextLine != null && reader.getCurrentLineNumber() != 0) {
-                throw new ParserException(e.getMessage(), e, reader.getCurrentLineNumber(), nextLine);
-            } else {
-                throw new RuntimeException(e);
-            }
-        } finally {
-            if (reader == null) {
-                reader.close();
-            }
-        }
-    }
-
-
-    /**
-     * Note:  This is an exact copy of the method in GCTDatasetParser.  Refactor to merge these
-     * two parsers, or share a common base class.
-     *
-     * @param comment
-     * @param dataset
-     */
-    private void parseComment(String comment, SegmentedAsciiDataSet dataset) {
-
-        String tmp = comment.substring(1, comment.length());
-        String[] tokens = tmp.split("=");
-        if (tokens.length != 2) {
-            return;
-        }
-        String key = tokens[0].trim().toLowerCase();
-        if (key.equals("type")) {
-
-            try {
-                dataset.setTrackType(TrackType.valueOf(tokens[1].trim().toUpperCase()));
-            } catch (Exception exception) {
-
-                // Ignore
-
-            }
-        }
-    }
-}
diff --git a/src/org/broad/igv/data/SegmentedAsciiDataSet.java b/src/org/broad/igv/data/SegmentedAsciiDataSet.java
deleted file mode 100644
index e770afe..0000000
--- a/src/org/broad/igv/data/SegmentedAsciiDataSet.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * sample       chrom   loc.start       loc.end num.mark        num.informative seg.mean
-TCGA-02-0001-01C-01D-0183-04    1       554268  74674720        6892    6721    0.2077
-TCGA-02-0001-01C-01D-0183-04    1       74693652        75110251        37      37      -0.2659
- */
-package org.broad.igv.data;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import org.broad.igv.feature.Genome;
-import org.broad.igv.feature.LocusScore;
-import org.broad.igv.track.TrackType;
-import org.broad.igv.ui.IGVModel;
-import org.broad.igv.util.ResourceLocator;
-
-import java.util.*;
-
-
-/**
- * @author jrobinso
- */
-public class SegmentedAsciiDataSet implements SegmentedDataSet {
-
-    SegmentFileParser parser;
-    TrackType trackType = TrackType.COPY_NUMBER;
-    float dataMax = -Float.MAX_VALUE;
-    float dataMin = Float.MAX_VALUE;
-    /**
-     * Assume data is non-log value until suggested otherwise by the precense
-     * of negative numbers.  TODO This is a fragile assumption, the user should
-     * input this information directly.
-     */
-    boolean logNormalized = false;
-    /**
-     * Map of [heading ->  [chr -> [list of chrSegments]]]
-     */
-    Map<String, Map<String, List<LocusScore>>> segments = new HashMap();
-    /**
-     * Set of chromosomes represented in this dataset
-     */
-    Set<String> chromosomes = new HashSet();
-    List<String> headings = new ArrayList();
-    private Map<String, List<LocusScore>> wholeGenomeScoresCache = new HashMap();
-    private long lastRefreshTime = 0;
-
-    /**
-     * Constructs ...
-     *
-     * @param locator
-     */
-    public SegmentedAsciiDataSet(ResourceLocator locator) {
-        parser = new SegmentFileParser(locator);
-        parser.loadSegmentTracks(this);
-
-    }
-
-    /**
-     * Method description
-     *
-     * @param timestamp
-     */
-    public synchronized void refreshData(long timestamp) {
-        if (timestamp > lastRefreshTime) {
-            dataMax = -Float.MAX_VALUE;
-            dataMin = Float.MAX_VALUE;
-            logNormalized = false;
-            segments = new HashMap();
-            headings = new ArrayList();
-            wholeGenomeScoresCache.clear();
-            parser.loadSegmentTracks(this);
-            lastRefreshTime = timestamp;
-        }
-
-    }
-
-    /**
-     * Method description
-     *
-     * @param heading
-     * @param chr
-     * @param start
-     * @param end
-     * @param value
-     * @param snpCount
-     */
-    public void addSegment(String heading, String chr, int start, int end, float value,
-                           int snpCount) {
-
-        Map<String, List<LocusScore>> chrSegments = segments.get(heading);
-        if (chrSegments == null) {
-            headings.add(heading);
-            chrSegments = new HashMap();
-            segments.put(heading, chrSegments);
-        }
-
-        List<LocusScore> segmentList = chrSegments.get(chr);
-        if (segmentList == null) {
-            segmentList = new ArrayList<LocusScore>();
-            chrSegments.put(chr, segmentList);
-        }
-        segmentList.add(new Segment(start, start, end, end, value, snpCount));
-        dataMax = Math.max(dataMax, value);
-        dataMin = Math.min(dataMin, value);
-        if (value < 0) {
-            logNormalized = true;
-        }
-
-        chromosomes.add(chr);
-
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public Set<String> getChromosomes() {
-        return chromosomes;
-    }
-
-    /**
-     * Method description
-     *
-     * @param heading
-     * @param chr
-     * @return
-     */
-    public List<LocusScore> getSegments(String heading, String chr) {
-        Map<String, List<LocusScore>> chrSegments = segments.get(heading);
-        return (chrSegments == null) ? null : chrSegments.get(chr);
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public List<String> getDataHeadings() {
-        return headings;
-    }
-
-
-    public List<String> getSampleNames() {
-        return headings;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public TrackType getType() {
-        return trackType;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public boolean isLogNormalized() {
-        return logNormalized;
-    }
-
-    /**
-     * Method description
-     *
-     * @param chr
-     * @return
-     */
-    public double getDataMax(String chr) {
-        return dataMax;
-    }
-
-    /**
-     * Method description
-     *
-     * @param chr
-     * @return
-     */
-    public double getDataMin(String chr) {
-        return dataMin;
-    }
-
-    /**
-     * Method description
-     *
-     * @param heading
-     * @return
-     */
-    public List<LocusScore> getWholeGenomeScores(String heading) {
-
-
-        List<LocusScore> wholeGenomeScores = wholeGenomeScoresCache.get(heading);
-        if ((wholeGenomeScores == null) || wholeGenomeScores.isEmpty()) {
-            int locationUnit = 1000;
-            Genome genome = IGVModel.getInstance().getViewContext().getGenome();
-
-            // Compute the smallest concievable feature that could be viewed on the
-            // largest screen.  Be conservative.   The smallest feature is one at
-            // the screen resolution scale in <chr units> / <pixel>
-            int maxScreenSize = 4000;
-            double minFeatureSize = ((double) genome.getLength()) / (maxScreenSize * locationUnit);
-
-            long offset = 0;
-            wholeGenomeScores = new ArrayList(1000);
-            for (String chr : genome.getChromosomeNames()) {
-                List<LocusScore> chrSegments = getSegments(heading, chr);
-                if (chrSegments != null) {
-                    int lastgEnd = -1;
-                    for (LocusScore score : chrSegments) {
-                        Segment seg = (Segment) score;
-                        int gStart = (int) ((offset + score.getStart()) / locationUnit);
-                        int gEnd = (int) ((offset + score.getEnd()) / locationUnit);
-                        if ((gEnd - gStart) > minFeatureSize) {
-                            wholeGenomeScores.add(new Segment(gStart, gStart, gEnd,
-                                    gEnd, seg.getScore(), seg.getSnpCount()));
-                        }
-                    }
-
-                }
-                offset += genome.getChromosome(chr).getLength();
-            }
-            wholeGenomeScoresCache.put(heading, wholeGenomeScores);
-        }
-        return wholeGenomeScores;
-
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public TrackType getTrackType() {
-        return trackType;
-    }
-
-    /**
-     * Method description
-     *
-     * @param trackType
-     */
-    public void setTrackType(TrackType trackType) {
-        this.trackType = trackType;
-    }
-
-
-}
diff --git a/src/org/broad/igv/data/SegmentedBinaryDataSet.java b/src/org/broad/igv/data/SegmentedBinaryDataSet.java
deleted file mode 100644
index 52dbc69..0000000
--- a/src/org/broad/igv/data/SegmentedBinaryDataSet.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-package org.broad.igv.data;
-
-import org.broad.igv.IGVConstants;
-import org.broad.igv.feature.LocusScore;
-import org.broad.igv.track.TrackType;
-import org.broad.igv.ui.IGVModel;
-import org.broad.igv.util.ResourceLocator;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * @author jrobinso
- */
-public class SegmentedBinaryDataSet implements SegmentedDataSet {
-
-    SegmentedBinaryReader reader;
-
-    private long lastRefreshTime = 0;
-
-    /**
-     * Assume data is non-log value until suggested otherwise by the precense
-     * of negative numbers.  TODO This is a fragile assumption, the user should
-     * input this information directly.
-     */
-    Boolean logNormalized = null;
-
-    private List<String> sampleNames = null;
-
-    /**
-     * Map of [heading ->  [chr -> [list of segmentsCache]]]
-     */
-    Map<String, Map<String, List<LocusScore>>> segmentsCache = new HashMap();
-
-    Map<String, SegmentedChromosomeData> chrData = new HashMap();
-
-    TrackType type = TrackType.COPY_NUMBER;
-
-    public SegmentedBinaryDataSet(ResourceLocator locator) {
-        reader = locator.isLocal() ? new SegmentedBinaryLocalReader(locator.getPath()) : new SegmentedBinaryRemoteReader(locator);
-
-        try {
-            type = TrackType.valueOf(reader.getStringAttribute("type"));
-        }
-        catch (Exception e) {
-            //ignore;
-        }
-
-        try {
-            String logNormalizedString = reader.getStringAttribute("logNormalized");
-            if (logNormalizedString != null) {
-                logNormalized = logNormalizedString.equals("true");
-            }
-
-        } catch (Exception exception) {
-            // ignore
-        }
-
-        // TODO Temporary -- start loading whole genome scores ascynchonously
-        if (!IGVModel.getInstance().getViewContext().getChrName().equals(IGVConstants.CHR_ALL)) {
-            (new Thread(new Runnable() {
-
-                public void run() {
-                    synchronized (chrData) {
-                        SegmentedChromosomeData cd = reader.getChromosomeData(IGVConstants.CHR_ALL);
-                        chrData.put(IGVConstants.CHR_ALL, cd);
-                    }
-                }
-            })).start();
-        }
-
-    }
-
-    public synchronized void refreshData(long timestamp) {
-        if (timestamp > lastRefreshTime) {
-            logNormalized = false;
-            segmentsCache =
-                    new HashMap();
-            lastRefreshTime =
-                    timestamp;
-        }
-
-    }
-
-    // TODO -- the synchronized keyword can be removed with the async whole genome load
-    public List<LocusScore> getSegments(String heading, String chr) {
-
-        Map<String, List<LocusScore>> chrSegments = segmentsCache.get(heading);
-        if (chrSegments == null) {
-            chrSegments = new HashMap();
-            segmentsCache.put(heading, chrSegments);
-        }
-
-        List<LocusScore> segments = chrSegments.get(chr);
-
-        if (segments == null) {
-
-            SegmentedChromosomeData cd = chrData.get(chr);
-            if (cd == null) {
-                cd = reader.getChromosomeData(chr);
-                chrData.put(chr, cd);
-            }
-
-            int[] startLocations = cd.getStartLocations(heading);
-            int[] endLocations = cd.getEndLocations(heading);
-            float[] values = cd.getValues(heading);
-
-            if (startLocations == null || startLocations.length == 0) {
-                return null;
-            }
-
-            if (logNormalized == null) {
-                logNormalized = false;
-                for (int i = 0; i <
-                        values.length; i++) {
-                    if (values[i] < 0) {
-                        logNormalized = true;
-                        break;
-                    }
-
-                }
-            }
-            assert (startLocations.length == endLocations.length);
-            assert (endLocations.length == values.length);
-
-            segments = new ArrayList(startLocations.length);
-            for (int i = 0; i <
-                    startLocations.length; i++) {
-                segments.add(new Segment(startLocations[i], endLocations[i], values[i]));
-                chrSegments.put(chr, segments);
-            }
-
-        }
-        return segments;
-    }
-
-    public List<String> getSampleNames() {
-        if (sampleNames == null) {
-            sampleNames = reader.getSampleNames();
-        }
-
-        return sampleNames;
-    }
-
-    public TrackType getType() {
-        return type;
-    }
-
-    // TODO -- check for negative numbers
-    public boolean isLogNormalized() {
-        return logNormalized == null ? true : logNormalized;
-    }
-
-    public double getDataMax(String chr) {
-        return 3;
-    }
-
-    public double getDataMin(String chr) {
-        return -3;
-    }
-
-    public synchronized List<LocusScore> getWholeGenomeScores(String heading) {
-        return getSegments(heading, IGVConstants.CHR_ALL);
-
-    }
-
-    public static class ChromosomeChunk {
-    }
-
-}
diff --git a/src/org/broad/igv/data/SegmentedBinaryLocalReader.java b/src/org/broad/igv/data/SegmentedBinaryLocalReader.java
deleted file mode 100644
index 05c05c0..0000000
--- a/src/org/broad/igv/data/SegmentedBinaryLocalReader.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.data;
-
-import java.io.*;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-
-/**
- * @author jrobinso
- */
-public class SegmentedBinaryLocalReader implements SegmentedBinaryReader {
-
-    String filePath;
-
-    List<String> chromosomes;
-
-    List<String> sampleNames;
-
-    Map<String, String> dataFileMap = new HashMap();
-
-    Map<String, String> attrs;
-
-    public SegmentedBinaryLocalReader(String filePath) {
-        this.filePath = filePath;
-    }
-
-    /**
-     * Return the set of sample names in this data set
-     *
-     * @return
-     */
-    public List<String> getSampleNames() {
-        if (sampleNames == null) {
-            sampleNames = readAsStrings("data/samples.txt");
-        }
-        return sampleNames;
-
-    }
-
-    public String getStringAttribute(String key) {
-        return getAttributes().get(key);
-    }
-
-    public Map<String, String> getAttributes() {
-        if (attrs == null) {
-            attrs = new HashMap();
-            List<String> lines = readAsStrings("data/attributes.txt");
-            if (lines != null) {
-                for (String kv : lines) {
-                    String[] tokens = kv.split("=");
-                    attrs.put(tokens[0], tokens[1]);
-                }
-            }
-        }
-        return attrs;
-
-    }
-
-    public SegmentedChromosomeData getChromosomeData(String chr) {
-
-        return readChromosomeData(chr);
-
-    }
-
-    /**
-     * Return the segment start locations for the sample and chromosome
-     *
-     * @param sample the sample name
-     * @param chr    the chromosome name
-     * @return array of end locations
-     */
-    public int[] getStartLocations(String sample, String chr) {
-        String entryName = "data/" + sample + "/" + chr + "/starts.bin";
-        return readAsInts(entryName);
-    }
-
-    /**
-     * Return the segment end locations for the sample and chromosome
-     *
-     * @param sample the sample name
-     * @param chr    the chromosome name
-     * @return array of end locations
-     */
-    public int[] getEndLocations(String sample, String chr) {
-        String entryName = "data/" + sample + "/" + chr + "/ends.bin";
-        return readAsInts(entryName);
-    }
-
-    /**
-     * Return the data values for the given sample and chromosom
-     *
-     * @param sample the sample name
-     * @param chr    the chromosome name
-     * @return data values
-     */
-    public float[] getValues(String sample, String chr) {
-        String entryName = "data/" + sample + "/" + chr + "/values.bin";
-        return readAsFloats(entryName);
-    }
-
-    private List<String> readAsStrings(String entryName) {
-        List<String> strings = new ArrayList();
-        ZipFile zipFile = null;
-        try {
-            zipFile = new ZipFile(new File(filePath));
-
-            ZipEntry entry = zipFile.getEntry(entryName);
-            if (entry == null) {
-                return null;
-            }
-
-            BufferedReader br = new BufferedReader(new InputStreamReader(zipFile.getInputStream(entry)));
-            String nextLine = null;
-            while ((nextLine = br.readLine()) != null) {
-                strings.add(nextLine.trim());
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-        } finally {
-            try {
-                zipFile.close();
-            } catch (IOException e) {
-                // Close quietly
-            }
-
-        }
-        return strings;
-
-    }
-
-    private SegmentedChromosomeData readChromosomeData(String chr) {
-        ZipFile zipFile = null;
-        BufferedInputStream is = null;
-        try {
-            String entryName = "data/" + chr + ".bin";
-            zipFile = new ZipFile(new File(filePath));
-            ZipEntry entry = zipFile.getEntry(entryName);
-            if (entry == null) {
-                return null;
-            }
-
-            is = new BufferedInputStream(zipFile.getInputStream(entry));
-
-            SegmentedChromosomeData cd = new SegmentedChromosomeData();
-            cd.deserialize(is);
-            return cd;
-
-        } catch (IOException e) {
-            e.printStackTrace();
-            return null;
-        } finally {
-            try {
-                zipFile.close();
-            } catch (IOException e) {
-                // Close quietly
-            }
-        }
-
-    }
-
-    /**
-     * Read the contents of a zip entry as an array of ints.
-     *
-     * @param entryName name of the entry
-     * @return array of ints
-     */
-    private int[] readAsInts(String entryName) {
-
-        ZipFile zipFile = null;
-        try {
-            zipFile = new ZipFile(new File(filePath));
-            ZipEntry entry = zipFile.getEntry(entryName);
-            if (entry == null) {
-                return null;
-            }
-
-            DataInputStream is = new DataInputStream(
-                    new BufferedInputStream(zipFile.getInputStream(entry)));
-
-            int nInts = (int) (entry.getSize() / 4);
-            int[] ints = new int[nInts];
-            for (int i = 0; i <
-                    nInts; i++) {
-                ints[i] = is.readInt();
-            }
-
-            return ints;
-        } catch (IOException e) {
-            e.printStackTrace();
-            return null;
-        } finally {
-            try {
-                zipFile.close();
-            } catch (IOException e) {
-                // close quietly
-            }
-        }
-
-    }
-
-    /**
-     * Read the content of a zip entry as an array of floats.
-     *
-     * @param entryName name of the entry
-     * @return contents as an array of floats
-     */
-    private float[] readAsFloats(String entryName) {
-
-        ZipFile zipFile = null;
-        try {
-            zipFile = new ZipFile(new File(filePath));
-            ZipEntry entry = zipFile.getEntry(entryName);
-            if (entry == null) {
-                return null;
-            }
-
-            DataInputStream is = new DataInputStream(
-                    new BufferedInputStream(zipFile.getInputStream(entry)));
-
-            int nInts = (int) (entry.getSize() / 4);
-            float[] values = new float[nInts];
-            for (int i = 0; i <
-                    nInts; i++) {
-                values[i] = is.readFloat();
-            }
-
-            return values;
-        } catch (IOException e) {
-            e.printStackTrace();
-            return null;
-        } finally {
-            try {
-                zipFile.close();
-            } catch (IOException e) {
-
-            }
-        }
-
-
-    }
-
-}
-
-
diff --git a/src/org/broad/igv/data/SegmentedBinaryReader.java b/src/org/broad/igv/data/SegmentedBinaryReader.java
deleted file mode 100644
index 3262ade..0000000
--- a/src/org/broad/igv/data/SegmentedBinaryReader.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-package org.broad.igv.data;
-
-import java.util.List;
-
-/**
- * @author jrobinso
- */
-public interface SegmentedBinaryReader {
-
-    SegmentedChromosomeData getChromosomeData(String chr);
-
-
-    /**
-     * Return the set of sample names in this data set
-     *
-     * @return
-     */
-    List<String> getSampleNames();
-
-    public String getStringAttribute(String key);
-
-}
diff --git a/src/org/broad/igv/data/SegmentedBinaryRemoteReader.java b/src/org/broad/igv/data/SegmentedBinaryRemoteReader.java
deleted file mode 100644
index b40ae36..0000000
--- a/src/org/broad/igv/data/SegmentedBinaryRemoteReader.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.data;
-
-import org.apache.log4j.Logger;
-import org.broad.igv.util.ResourceLocator;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * @author jrobinso
- */
-public class SegmentedBinaryRemoteReader implements SegmentedBinaryReader {
-
-    static Logger log = Logger.getLogger(SegmentedBinaryRemoteReader.class);
-
-    String serverURL;
-
-    String filePath;
-
-    Map<String, String> attributes;
-
-    public SegmentedBinaryRemoteReader(ResourceLocator locator) {
-        this.serverURL = locator.getServerURL();
-        this.filePath = locator.getPath();
-    }
-
-    public SegmentedChromosomeData getChromosomeData(String chr) {
-
-        InputStream is = null;
-        try {
-            URL url = new URL(serverURL + "?method=getChromosomeData&file=" +
-                    filePath + "&chr=" + chr);
-            URLConnection connection = url.openConnection();
-            SegmentedChromosomeData cd = new SegmentedChromosomeData();
-            is = connection.getInputStream();
-            cd.deserialize(is);
-            return cd;
-        }
-        catch (IOException ex) {
-            log.error("Error opening file", ex);
-            throw new RuntimeException(ex);
-        } finally {
-            if (is != null) {
-                try {
-                    is.close();
-
-                } catch (IOException iOException) {
-                    log.error("Error closing URL stream", iOException);
-                }
-            }
-
-        }
-    }
-
-    public List<String> getSampleNames() {
-        InputStream urlStream = null;
-
-        try {
-            List<String> childNames = new ArrayList(100);
-            URL url = new URL(serverURL + "?method=getSampleNames&file=" + filePath);
-            URLConnection connection = url.openConnection();
-
-            urlStream = connection.getInputStream();
-            BufferedReader reader = new BufferedReader(new InputStreamReader(urlStream));
-            String nextLine = "";
-            while ((nextLine = reader.readLine()) != null) {
-                childNames.add(nextLine.trim());
-            }
-            reader.close();
-            return childNames;
-
-        } catch (IOException ex) {
-            //log.error("Error in getChildNames", ex);   
-            throw new RuntimeException(ex);
-        } finally {
-            if (urlStream != null) {
-                try {
-                    urlStream.close();
-
-                } catch (IOException iOException) {
-                    log.error("Error closing URL stream", iOException);
-                }
-            }
-
-        }
-    }
-    /*
-     *             
-    List<String> lines = readAsStrings("data/attributes.txt");
-    if (lines != null) {
-    for (String kv : lines) {
-    String[] tokens = kv.split("\t");
-    attrs.put(tokens[0], tokens[1]);
-    }
-    }
-     */
-
-    public String getStringAttribute(String key) {
-        if (attributes == null) {
-            attributes = new HashMap();
-            InputStream urlStream = null;
-
-            try {
-                URL url = new URL(serverURL + "?method=getAttributes&file=" + filePath);
-                URLConnection connection = url.openConnection();
-
-                urlStream = connection.getInputStream();
-                BufferedReader reader = new BufferedReader(new InputStreamReader(urlStream));
-                String nextLine = "";
-                while ((nextLine = reader.readLine()) != null) {
-                    String[] tokens = nextLine.split("=");
-                    if (tokens.length > 1) {
-                        attributes.put(tokens[0], tokens[1]);
-                    }
-                }
-
-                reader.close();
-
-            } catch (IOException ex) {
-                log.error("Error in getChildNames", ex);
-
-            } finally {
-                if (urlStream != null) {
-                    try {
-                        urlStream.close();
-
-                    } catch (IOException iOException) {
-                        log.error("Error closing URL stream", iOException);
-                    }
-                }
-
-            }
-        }
-        return attributes.get(key);
-
-    }
-}
diff --git a/src/org/broad/igv/data/SegmentedChromosomeData.java b/src/org/broad/igv/data/SegmentedChromosomeData.java
deleted file mode 100644
index 6aa423d..0000000
--- a/src/org/broad/igv/data/SegmentedChromosomeData.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.data;
-
-import org.apache.log4j.Logger;
-
-import java.io.*;
-import java.util.HashMap;
-import java.util.Map;
-
-
-/**
- * This class encapuslates data for a single chromosome from a segmented
- * dataset.   Its main purpose is to provide a single object that can
- * easily be serialized and passed from server to IGV clients.
- *
- * @author jrobinso
- */
-public class SegmentedChromosomeData {
-
-    private static Logger log = Logger.getLogger(SegmentedChromosomeData.class);
-
-    private String[] sampleNames;
-
-    Map<String, int[]> startLocations;
-
-    Map<String, int[]> endLocations;
-
-    Map<String, float[]> valueMap;
-
-    public SegmentedChromosomeData() {
-    }
-
-    public SegmentedChromosomeData(
-            String[] sampleNames,
-            Map<String, int[]> startLocations,
-            Map<String, int[]> endLocations,
-            Map<String, float[]> valueMap) {
-        this.sampleNames = sampleNames;
-        this.startLocations = startLocations;
-        this.endLocations = endLocations;
-        this.valueMap = valueMap;
-    }
-
-    /**
-     * Serialize this instance to a stream.
-     * <p/>
-     * Format:
-     * Sample names as a string :
-     * sample1 tab sample2 tabnetc...  line-feed
-     * Followed by (for each sample)
-     * numberOfPoints (int)
-     * start locations (int array)
-     * end locations (int array)
-     * values (float array)
-     *
-     * @param stream stream to serialize to
-     */
-    public void serialize(OutputStream stream) {
-
-        try {
-            DataOutputStream os = new DataOutputStream(new BufferedOutputStream(stream));
-
-            for (String sample : getSampleNames()) {
-                os.writeChars(sample);
-                os.writeChar('\t');
-            }
-            os.writeChar('\n');
-            for (String sample : getSampleNames()) {
-                int[] starts = startLocations.get(sample);
-                int[] ends = endLocations.get(sample);
-                float[] values = valueMap.get(sample);
-                int nPts = starts.length;
-
-                assert ends.length == nPts;
-                assert values.length == nPts;
-
-                os.writeInt(nPts);
-                for (int i = 0; i < nPts; i++) {
-                    os.writeInt(starts[i]);
-                }
-                for (int i = 0; i < nPts; i++) {
-                    os.writeInt(ends[i]);
-                }
-                for (int i = 0; i < nPts; i++) {
-                    os.writeFloat(values[i]);
-                }
-            }
-            os.flush();
-
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-
-    /**
-     * Initialize the object by deserializing the stream.
-     *
-     * @param stream
-     * @see #serialize
-     */
-    public void deserialize(InputStream stream) {
-        startLocations = new HashMap();
-        endLocations = new HashMap();
-        valueMap = new HashMap();
-        try {
-
-            DataInputStream is = new DataInputStream(new BufferedInputStream(stream));
-
-            StringArrayList strings = new StringArrayList(100);
-            char c = is.readChar();
-            while (c != '\n') {
-                CharArrayList chars = new CharArrayList(10);
-                while (c != '\t') {
-                    chars.add(c);
-                    c = is.readChar();
-                }
-                String nm = new String(chars.toArray()).trim();
-                strings.add(nm);
-                c = is.readChar();
-            }
-            sampleNames = strings.toArray();
-
-            for (String sample : getSampleNames()) {
-                int nPts = is.readInt();
-                int[] starts = new int[nPts];
-                for (int i = 0; i < nPts; i++) {
-                    starts[i] = is.readInt();
-                }
-                int[] ends = new int[nPts];
-                for (int i = 0; i < nPts; i++) {
-                    ends[i] = is.readInt();
-                }
-                float[] values = new float[nPts];
-                for (int i = 0; i < nPts; i++) {
-                    values[i] = is.readFloat();
-                }
-                startLocations.put(sample, starts);
-                endLocations.put(sample, ends);
-                valueMap.put(sample, values);
-            }
-
-        }
-        catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-
-    public String[] getSampleNames() {
-        return sampleNames;
-    }
-
-    public int[] getStartLocations(String sampleName) {
-        return startLocations.get(sampleName);
-    }
-
-    public int[] getEndLocations(String sampleName) {
-        return endLocations.get(sampleName);
-    }
-
-    public float[] getValues(String sampleName) {
-        return valueMap.get(sampleName);
-    }
-}
diff --git a/src/org/broad/igv/data/SegmentedDataSet.java b/src/org/broad/igv/data/SegmentedDataSet.java
deleted file mode 100644
index 3a9c316..0000000
--- a/src/org/broad/igv/data/SegmentedDataSet.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-package org.broad.igv.data;
-
-import org.broad.igv.feature.LocusScore;
-import org.broad.igv.track.TrackType;
-
-import java.util.List;
-
-/**
- * @author jrobinso
- */
-public interface SegmentedDataSet {
-
-    double getDataMax(String chr);
-
-    double getDataMin(String chr);
-
-    List<String> getSampleNames();
-
-    List<LocusScore> getSegments(String heading, String chr);
-
-    TrackType getType();
-
-    List<LocusScore> getWholeGenomeScores(String heading);
-
-    boolean isLogNormalized();
-
-    void refreshData(long timestamp);
-
-}
diff --git a/src/org/broad/igv/data/SegmentedDataSource.java b/src/org/broad/igv/data/SegmentedDataSource.java
deleted file mode 100644
index 8d88bde..0000000
--- a/src/org/broad/igv/data/SegmentedDataSource.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.data;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import org.broad.igv.IGVConstants;
-import org.broad.igv.feature.LocusScore;
-import org.broad.igv.track.TrackType;
-import org.broad.igv.track.WindowFunction;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * TODO -- THIS IS NEARLY AN EXACT COPY OF SegmentedDataSource,  only the
- * dataset type is different.
- *
- * @author jrobinso
- */
-public class SegmentedDataSource implements DataSource {
-
-    /**
-     * Identifies the track.  Often a sample name.
-     */
-    String trackIdentifier;
-    SegmentedDataSet dataset;
-
-    /**
-     * Constructs ...
-     *
-     * @param trackIdentifier
-     * @param dataset
-     */
-    public SegmentedDataSource(String trackIdentifier, SegmentedDataSet dataset) {
-        this.trackIdentifier = trackIdentifier;
-        this.dataset = dataset;
-    }
-
-    /**
-     * Method description
-     *
-     * @param timestamp
-     */
-    public void refreshData(long timestamp) {
-        dataset.refreshData(timestamp);
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public TrackType getTrackType() {
-        return dataset.getType();
-    }
-
-    /**
-     * Method description
-     *
-     * @param statType
-     */
-    public void setWindowFunction(WindowFunction statType) {
-
-        // ignore
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public boolean isLogNormalized() {
-        return dataset.isLogNormalized();
-    }
-
-    /**
-     * Method description
-     *
-     * @param chr
-     * @return
-     */
-    public double getDataMax(String chr) {
-        return dataset.getDataMax(chr);
-    }
-
-    /**
-     * Method description
-     *
-     * @param chr
-     * @return
-     */
-    public double getDataMin(String chr) {
-        return dataset.getDataMin(chr);
-    }
-
-    /**
-     * Method description
-     *
-     * @param zoom
-     * @param chr
-     * @return
-     */
-    public double getMedian(int zoom, String chr) {
-        return 1.0;
-    }
-
-    private List<LocusScore> getSegments(String chr) {
-
-        return dataset.getSegments(trackIdentifier, chr);
-
-    }
-
-    /**
-     * Method description
-     *
-     * @param chr
-     * @param startLocation
-     * @param endLocation
-     * @param zoom
-     * @return
-     */
-    public List<LocusScore> getSummaryScoresForRange(String chr, int startLocation,
-                                                     int endLocation, int zoom) {
-        if (chr.equals(IGVConstants.CHR_ALL)) {
-            return getWholeGenomeScores();
-        }
-        return getSegments(chr);
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public List<LocusScore> getWholeGenomeScores() {
-        return dataset.getWholeGenomeScores(trackIdentifier);
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public Collection<WindowFunction> getAvailableWindowFunctions() {
-        return new ArrayList();
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public WindowFunction getWindowFunction() {
-        return null;
-    }
-}
diff --git a/src/org/broad/igv/data/SegmentedDataWriter.java b/src/org/broad/igv/data/SegmentedDataWriter.java
deleted file mode 100644
index 01f2a1c..0000000
--- a/src/org/broad/igv/data/SegmentedDataWriter.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.data;
-
-import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
-import org.broad.igv.feature.LocusScore;
-import org.broad.igv.track.TrackType;
-import org.broad.igv.ui.IGVModel;
-import org.broad.igv.util.ResourceLocator;
-import org.broad.igv.util.Utilities;
-
-import java.io.*;
-import java.util.*;
-import java.util.zip.ZipOutputStream;
-
-/**
- * @author jrobinso
- */
-public class SegmentedDataWriter {
-
-    private static Logger log = Logger.getLogger(SegmentedDataWriter.class);
-
-    private boolean compress = true;
-
-    SegmentedAsciiDataSet dataset;
-
-    String rootNodeName;
-
-    File outputFile;
-
-    TrackType trackType;
-
-    public SegmentedDataWriter(SegmentedAsciiDataSet dataset, File outputFile) {
-        this(dataset, outputFile, false, TrackType.COPY_NUMBER);
-    }
-
-    public SegmentedDataWriter(SegmentedAsciiDataSet dataset, File outputFile, boolean compress, TrackType type) {
-        this.dataset = dataset;
-        this.compress = compress;
-        this.outputFile = outputFile;
-        this.rootNodeName = "data";
-        this.trackType = type;
-    }
-
-    public void writeContents() {
-
-        ZipOutputStream zos = null;
-
-        try {
-            zos = new ZipOutputStream(new FileOutputStream(outputFile));
-            List<String> samples = dataset.getDataHeadings();
-            String[] sampleArray = samples.toArray(new String[]{});
-            Set<String> chromosomes = dataset.getChromosomes();
-
-            List<String> attributes = new ArrayList();
-            attributes.add("type=" + trackType.toString());
-            attributes.add("logNormalized=" + dataset.isLogNormalized());
-            writeStrings("attributes.txt", attributes, zos);
-
-            writeStrings("samples.txt", samples, zos);
-            writeStrings("chromosomes.txt", chromosomes, zos);
-
-
-            ArrayList<String> tmp = new ArrayList(dataset.getChromosomes());
-            tmp.add(IGVConstants.CHR_ALL);
-            for (String chr : tmp) {
-                Map<String, int[]> starts = new HashMap();
-                Map<String, int[]> ends = new HashMap();
-                Map<String, float[]> values = new HashMap();
-                for (String sample : samples) {
-
-                    List<LocusScore> segments = chr.equals(IGVConstants.CHR_ALL) ? dataset.getWholeGenomeScores(sample) : dataset.getSegments(sample, chr);
-                    int nPts = segments == null ? 0 : segments.size();
-                    int[] s = new int[nPts];
-                    int[] e = new int[nPts];
-                    float[] v = new float[nPts];
-                    for (int i = 0; i < nPts; i++) {
-                        s[i] = segments.get(i).getStart();
-                        e[i] = segments.get(i).getEnd();
-                        v[i] = segments.get(i).getScore();
-                    }
-
-                    starts.put(sample, s);
-                    ends.put(sample, e);
-                    values.put(sample, v);
-
-                    //writeSegments(sample, chr, segments, zos);
-                }
-                SegmentedChromosomeData cd = new SegmentedChromosomeData(
-                        sampleArray, starts, ends, values);
-                writeChromsomeData(chr, cd, zos);
-
-            }
-
-        } catch (IOException e) {
-            e.printStackTrace();
-
-        } finally {
-            try {
-                zos.close();
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-        }
-    }
-
-    private void writeStrings(String filename, Collection<String> strings, ZipOutputStream zos) {
-        try {
-            // Put strings in a byte buffer.  We need to know the total size in bytes
-            StringBuffer buff = new StringBuffer();
-            for (String s : strings) {
-                buff.append(s);
-                buff.append('\n');
-            }
-            byte[] bytes = buff.toString().getBytes();
-
-            String entryName = rootNodeName + "/" + filename;
-            Utilities.createZipEntry(entryName, bytes, zos, compress);
-
-        } catch (IOException ex) {
-            ex.printStackTrace();
-        } finally {
-        }
-    }
-
-    /**
-     * Write the segment data from a single chromosome for a sample.  The data
-     * is organized in the following hierarchical structure:
-     * <p/>
-     * data/<sample>/<chr>/starts.bin
-     * data/<sample>/<chr>/ends.bin
-     * data/<sample>/<chr>/values.bin
-     *
-     * @param sampleName
-     * @param chr
-     * @param segments
-     */
-    private void writeChromsomeData(String chr, SegmentedChromosomeData cd, ZipOutputStream zos) {
-        try {
-
-            ByteArrayOutputStream bytes = new ByteArrayOutputStream(1000);
-            BufferedOutputStream bos = new BufferedOutputStream(bytes);
-            cd.serialize(bytes);
-            bos.close();
-
-            byte[] byteArray = bytes.toByteArray();
-
-            String entryName = rootNodeName + "/" + chr + ".bin";
-            Utilities.createZipEntry(entryName, byteArray, zos, compress);
-
-        } catch (IOException ex) {
-            log.error("Error writing segments", ex);
-        } finally {
-        }
-    }
-
-    public static void main(String[] args) {
-
-
-        String inputFile = null;
-        String outputFile = null;
-        String genomeId = null;
-        boolean compress = true;
-        TrackType trackType = TrackType.COPY_NUMBER;
-        if (args.length > 2) {
-            inputFile = args[0].trim();
-            outputFile = args[1].trim();
-            genomeId = args[2].trim();
-        } else {
-            System.out.println("Arguments: inputFile  outputFile  genomeId  [track type (optional)");
-            System.exit(-1);
-        }
-        if (args.length > 3) {
-            try {
-                trackType = TrackType.valueOf(args[3].trim());
-            } catch (Exception e) {
-                System.out.println("Unknown track type: " + args[3]);
-            }
-        }
-
-        System.out.println("Track type=" + trackType.toString());
-        //inputFile = "/Users/jrobinso/IGVTestData/Demo/TCGA/broad.seg";
-        //outputFile = "test.seg.zip";
-
-        // TODO -- remove the need for this hack
-        System.out.println("Setting genome: " + genomeId);
-        IGVModel.getInstance().getViewContext().setGenomeId(genomeId);
-
-        SegmentedAsciiDataSet ds = new SegmentedAsciiDataSet(new ResourceLocator(inputFile));
-
-        SegmentedDataWriter writer = new SegmentedDataWriter(ds, new File(outputFile), compress, trackType);
-
-        writer.writeContents();
-
-    }
-
-    public void setCompress(
-
-            boolean compress) {
-        this.compress = compress;
-    }
-}
diff --git a/src/org/broad/igv/data/StringArrayList.java b/src/org/broad/igv/data/StringArrayList.java
deleted file mode 100644
index 3367ca0..0000000
--- a/src/org/broad/igv/data/StringArrayList.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-package org.broad.igv.data;
-
-/**
- * @author jrobinso
- */
-public class StringArrayList {
-
-    static final int maxGrowIncrement = Integer.MAX_VALUE / 10;
-
-    int size = 0;
-    String[] values;
-
-    public StringArrayList(int maxSize) {
-        values = new String[maxSize];
-    }
-
-    public void add(String v) {
-        if (size >= values.length) {
-            grow();
-        }
-        values[size] = v;
-        size++;
-    }
-
-    public String[] toArray() {
-        trim();
-        return values;
-    }
-
-    private void grow() {
-        if (values.length >= Integer.MAX_VALUE) {
-            throw new RuntimeException("Maximum array size exceeded");
-        }
-        int increment = (int) (Math.max(1000, 0.2 * values.length));
-        int newSize = Math.min(Integer.MAX_VALUE, values.length + increment);
-        resize(newSize);
-
-    }
-
-    private void resize(int newSize) {
-        String[] tmp = new String[newSize];
-        System.arraycopy(values, 0, tmp, 0, Math.min(tmp.length, values.length));
-        values = tmp;
-    }
-
-    private void trim() {
-        resize(size);
-    }
-
-}
diff --git a/src/org/broad/igv/data/SummaryScore.java b/src/org/broad/igv/data/SummaryScore.java
index d586d13..0c3966f 100644
--- a/src/org/broad/igv/data/SummaryScore.java
+++ b/src/org/broad/igv/data/SummaryScore.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -57,6 +57,10 @@ public class SummaryScore implements LocusScore {
         this.start = start;
     }
 
+    public String getChr() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
     public int getStart() {
         return start;
     }
diff --git a/src/org/broad/igv/data/SummaryTile.java b/src/org/broad/igv/data/SummaryTile.java
index 7f8300c..a5bfdcc 100644
--- a/src/org/broad/igv/data/SummaryTile.java
+++ b/src/org/broad/igv/data/SummaryTile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -55,56 +55,32 @@ public class SummaryTile {
         summaryScores = new ArrayList(1000);
     }
 
-    /**
-     * Method description
-     *
-     * @param score
-     */
+
     public void addScore(LocusScore score) {
         summaryScores.add(score);
     }
 
-    /**
-     * Method description
-     *
-     * @param scores
-     */
+
     public void addAllScores(Collection<LocusScore> scores) {
         summaryScores.addAll(scores);
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public List<LocusScore> getScores() {
         return summaryScores;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public int getSize() {
         return summaryScores.size();
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public boolean isEmpty() {
         return summaryScores.isEmpty();
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public int getStartLocation() {
         return startLocation;
     }
diff --git a/src/org/broad/igv/data/SummaryTile2D.java b/src/org/broad/igv/data/SummaryTile2D.java
index 1ac3563..70361c9 100644
--- a/src/org/broad/igv/data/SummaryTile2D.java
+++ b/src/org/broad/igv/data/SummaryTile2D.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/data/TestData.java b/src/org/broad/igv/data/TestData.java
index 899c8f7..8b5264e 100644
--- a/src/org/broad/igv/data/TestData.java
+++ b/src/org/broad/igv/data/TestData.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -372,7 +372,7 @@ public class TestData {
 
     static public void generateGctTestFiles(File outputLocation) {
 
-        //ViewContext context = IGVModel.getInstance().getViewContext();
+        //ViewContext context = ViewContext.getInstance();
         Genome genome = GenomeManager.getInstance().getGenome(genomeId);
         if (genome == null) {
             throw new RuntimeException("Unknown genome: " + genomeId);
diff --git a/src/org/broad/igv/data/WiggleDataset.java b/src/org/broad/igv/data/WiggleDataset.java
index f9b6f33..61d179d 100644
--- a/src/org/broad/igv/data/WiggleDataset.java
+++ b/src/org/broad/igv/data/WiggleDataset.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,6 +23,10 @@ package org.broad.igv.data;
 
 //~--- non-JDK imports --------------------------------------------------------
 
+import org.broad.igv.util.collections.FloatArrayList;
+import org.broad.igv.util.collections.IntArrayList;
+import org.broad.igv.feature.Genome;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.track.TrackProperties;
 import org.broad.igv.track.TrackType;
 import org.broad.igv.util.ArrayHeapIntSorter;
@@ -43,9 +47,10 @@ public class WiggleDataset implements Dataset {
     Map<String, IntArrayList> startLocationsMap = new HashMap();
     Map<String, IntArrayList> endLocationsMap = new HashMap();
     Map<String, FloatArrayList> dataMap = new HashMap();
-    Map<String, Boolean> isSorted;
     float dataMin = 0;
     float dataMax = 0;
+    private Map<String, Integer> longestFeatureMap;
+
 
     /**
      * Constructs ...
@@ -60,13 +65,19 @@ public class WiggleDataset implements Dataset {
 
     }
 
+
     // TODO -- keep track of sortedness as data is loaded and skip this sort if unneccessary.
+
     public void sort(Set<String> unsortedChromosomes) {
+        Genome genome = ViewContext.getInstance().getGenome();
+        for (String c : unsortedChromosomes) {
+            String chr = genome.getChromosomeAlias(c);
 
-        for (String chr : unsortedChromosomes) {
-            final int[] starts = startLocationsMap.get(chr).toArray();
 
-            int[] indeces = new int[starts.length];
+            final IntArrayList starts = startLocationsMap.get(chr);
+            int sz = starts.size();
+
+            int[] indeces = new int[sz];
             for (int i = 0; i < indeces.length; i++) {
                 indeces[i] = i;
             }
@@ -74,32 +85,56 @@ public class WiggleDataset implements Dataset {
             (new ArrayHeapIntSorter()).sort(indeces, new IntComparator() {
 
                 public int compare(int arg0, int arg1) {
-                    return starts[arg0] - starts[arg1];
+                    return starts.get(arg0) - starts.get(arg1);
                 }
             });
 
 
-            startLocationsMap.get(chr).reorder(indeces);
-            endLocationsMap.get(chr).reorder(indeces);
-            dataMap.get(chr).reorder(indeces);
+            int[] sortedStarts = reorder(indeces, startLocationsMap.get(chr));
+            int[] sortedEnds = reorder(indeces, endLocationsMap.get(chr));
+            float[] sortedData = reorder(indeces, dataMap.get(chr));
+
+            startLocationsMap.put(chr, new IntArrayList(sortedStarts));
+            endLocationsMap.put(chr, new IntArrayList(sortedEnds));
+            dataMap.put(chr, new FloatArrayList(sortedData));
         }
 
     }
 
-    /**
-     * Method description
-     *
-     * @param chr
-     * @param starts
-     * @param ends
-     * @param dataMap
-     */
+    private float[] reorder(int[] indeces, FloatArrayList values) {
+        int size = values.size();
+        if (indeces.length != size) {
+            throw new IllegalArgumentException(
+                    "Index array length not equal to size");
+        }
+        float[] reorderedValues = new float[size];
+        for (int i = 0; i < size; i++) {
+            reorderedValues[i] = values.get(indeces[i]);
+        }
+        return reorderedValues;
+    }
+
+    private int[] reorder(int[] indeces, IntArrayList values) {
+        int size = values.size();
+        if (indeces.length != size) {
+            throw new IllegalArgumentException(
+                    "Index array length not equal to size");
+        }
+        int[] reorderedValues = new int[size];
+        for (int i = 0; i < size; i++) {
+            reorderedValues[i] = values.get(indeces[i]);
+        }
+        return reorderedValues;
+    }
+
+
     public void addDataChunk(String chr, IntArrayList starts, IntArrayList ends, FloatArrayList data) {
         IntArrayList startLocations = this.startLocationsMap.get(chr);
         if (startLocations == null) {
             this.startLocationsMap.put(chr, starts);
         } else {
-            startLocations.addAll(starts.toArray());
+            //starts.trimToSize();
+            startLocations.addAll(starts);
         }
 
         if (ends != null) {
@@ -107,7 +142,8 @@ public class WiggleDataset implements Dataset {
             if (endLocations == null) {
                 this.endLocationsMap.put(chr, ends);
             } else {
-                endLocations.addAll(ends.toArray());
+                //ends.trimToSize();
+                endLocations.addAll(ends);
             }
         }
 
@@ -115,7 +151,7 @@ public class WiggleDataset implements Dataset {
         if (dataArray == null) {
             this.dataMap.put(chr, data);
         } else {
-            dataArray.addAll(data.toArray());
+            dataArray.addAll(data);
         }
         float[] d = data.toArray();
         for (int i = 0; i < d.length; i++) {
@@ -126,146 +162,80 @@ public class WiggleDataset implements Dataset {
 
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public float getDataMin() {
         return dataMin;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public float getDataMax() {
         return dataMax;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public String getName() {
         return name;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public TrackType getType() {
         return TrackType.OTHER;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public String getGenome() {
         return genome;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public String[] getChromosomes() {
         return startLocationsMap.keySet().toArray(new String[]{});
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public String[] getDataHeadings() {
         return new String[]{getName()};
     }
 
-    /**
-     * Method description
-     *
-     * @param chr
-     * @return
-     */
+
     public int[] getStartLocations(String chr) {
         IntArrayList startLocations = this.startLocationsMap.get(chr);
-        return ((startLocations == null) ? null : startLocations.toArray());
+        if (startLocations == null) {
+            return null;
+        } else {
+            return startLocations.toArray();
+        }
     }
 
-    /**
-     * Method description
-     *
-     * @param chr
-     * @return
-     */
     public int[] getEndLocations(String chr) {
         IntArrayList endLocations = this.endLocationsMap.get(chr);
-        return ((endLocations == null) ? null : endLocations.toArray());
+        if (endLocations == null) {
+            return null;
+        } else {
+            return endLocations.toArray();
+        }
     }
 
-    /**
-     * Method description
-     *
-     * @param heading
-     * @param chr
-     * @return
-     */
     public float[] getData(String heading, String chr) {
         FloatArrayList data = this.dataMap.get(chr);
-        return ((data == null) ? null : data.toArray());
-
+        if (data == null) {
+            return null;
+        } else {
+            return data.toArray();
+        }
     }
 
-    /**
-     * Method description
-     *
-     * @param chr
-     * @return
-     */
     public String[] getFeatureNames(String chr) {
         return null;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public int getWindowSpan() {
         return 1;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public boolean isLogNormalized() {
         return false;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public boolean isLogValues() {
-        return false;
-    }
-
-    /**
-     * Method description
-     *
-     * @param name
-     */
     public void setName(String name) {
         this.name = name;
     }
@@ -273,4 +243,13 @@ public class WiggleDataset implements Dataset {
     public TrackProperties getTrackProperties() {
         return trackProperties;
     }
+
+    public Integer getLongestFeature(String chr) {
+        return longestFeatureMap == null ? 1000 :
+                longestFeatureMap.containsKey(chr) ? longestFeatureMap.get(chr) : 1;
+    }
+
+    public void setLongestFeatureMap(Map<String, Integer> longestFeatureMap) {
+        this.longestFeatureMap = longestFeatureMap;
+    }
 }
diff --git a/src/org/broad/igv/data/WiggleParser.java b/src/org/broad/igv/data/WiggleParser.java
index f07facd..52ce414 100644
--- a/src/org/broad/igv/data/WiggleParser.java
+++ b/src/org/broad/igv/data/WiggleParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -19,17 +19,22 @@ package org.broad.igv.data;
 
 //~--- non-JDK imports --------------------------------------------------------
 
+import org.broad.igv.util.collections.FloatArrayList;
+import org.broad.igv.util.collections.IntArrayList;
 import org.apache.log4j.Logger;
 import org.broad.igv.exceptions.ParserException;
-import org.broad.igv.util.ParsingUtils;
+import org.broad.igv.feature.Genome;
+import org.broad.igv.feature.GenomeManager;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.track.TrackProperties;
 import org.broad.igv.util.AsciiLineReader;
+import org.broad.igv.util.ParsingUtils;
 import org.broad.igv.util.ResourceLocator;
-import org.broad.igv.feature.Genome;
-import org.broad.igv.ui.IGVModel;
 
 import java.io.IOException;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -53,9 +58,7 @@ public class WiggleParser {
     /**
      * The type of wiggle locator (see UCSC documentation).
      */
-    private Type type
-
-            = Type.BED;
+    private Type type = Type.BED;
 
     // State variables.  This is a serial type parser,  these variables are used to hold temporary
     // state.
@@ -69,10 +72,11 @@ public class WiggleParser {
     IntArrayList startLocations = null;
     IntArrayList endLocations = null;
     FloatArrayList data = null;
-    private double minValue;
-    private double maxValue;
     ResourceLocator resourceLocator;
     Set<String> unsortedChromosomes;
+    int estArraySize;
+    Map<String, Integer> longestFeatureMap = new HashMap();
+
 
     /**
      * Constructs ...
@@ -81,8 +85,21 @@ public class WiggleParser {
      * @param genomeId
      */
     public WiggleParser(ResourceLocator locator, String genomeId) {
+
         this.resourceLocator = locator;
-        dataset = new WiggleDataset(genomeId, locator.getDisplayName());
+        this.estArraySize = estArraySize(locator, genomeId);
+        dataset = new WiggleDataset(genomeId, locator.getTrackName());
+    }
+
+    private int estArraySize(ResourceLocator locator, String genomeId) {
+
+        int estLines = 100000;
+        if (locator.getServerURL() == null) {
+            estLines = ParsingUtils.estimateLineCount(locator.getPath());
+        }
+        int nChromosomes = GenomeManager.getInstance().getGenome(genomeId).getChromosomeNames().size();
+        return Math.max(1000, (int) (estLines / nChromosomes));
+
     }
 
     /**
@@ -118,15 +135,12 @@ public class WiggleParser {
         return false;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public WiggleDataset parse() {
+
+
         String[] tokens = new String[10];
 
-        lastPosition = 0;
+        lastPosition = -1;
         unsortedChromosomes = new HashSet();
 
         AsciiLineReader reader = null;
@@ -139,10 +153,8 @@ public class WiggleParser {
 
             while ((nextLine = reader.readLine()) != null && (nextLine.trim().length() > 0)) {
 
-                if (nextLine.startsWith("#") || nextLine.startsWith("data") || nextLine.startsWith(
-                        "browser")) {
+                if (nextLine.startsWith("#") || nextLine.startsWith("data") || nextLine.startsWith("browser")) {
                     continue;
-
                     // Skip
                 }
 
@@ -176,7 +188,10 @@ public class WiggleParser {
                         continue;
                     }
                     try {
+
+
                         if (type.equals(Type.BED)) {
+
                             if (nTokens > 3) {
                                 chr = tokens[0].trim();
                                 if (!chr.equals(lastChr)) {
@@ -202,8 +217,12 @@ public class WiggleParser {
 
                                 startLocations.add(startPosition);
 
+
                                 try {
-                                    endLocations.add(Integer.parseInt(tokens[2].trim()));
+                                    int endPosition = Integer.parseInt(tokens[2].trim());
+                                    endLocations.add(endPosition);
+                                    int length = endPosition - startPosition;
+                                    updateLongestFeature(length);
                                 }
                                 catch (NumberFormatException numberFormatException) {
                                     log.error("Column 3  is not a number");
@@ -212,6 +231,7 @@ public class WiggleParser {
                                             reader.getCurrentLineNumber(), nextLine);
                                 }
 
+
                                 data.add(Float.parseFloat(tokens[3].trim()));
                             }
                         } else if (type.equals(Type.VARIABLE)) {
@@ -244,6 +264,8 @@ public class WiggleParser {
                         log.error(e);
                         throw new ParserException(e.getMessage(), reader.getCurrentLineNumber(), nextLine);
                     }
+
+
                 }
 
             }
@@ -268,12 +290,22 @@ public class WiggleParser {
         }
 
         dataset.sort(unsortedChromosomes);
+        dataset.setLongestFeatureMap(longestFeatureMap);
         return dataset;
     }
 
+    private void updateLongestFeature(int length) {
+        if (longestFeatureMap.containsKey(chr)) {
+            longestFeatureMap.put(chr, Math.max(longestFeatureMap.get(chr), length));
+        } else {
+            longestFeatureMap.put(chr, length);
+        }
+    }
+
     // fixedStep chrom=chrM strt=1 step=1
+
     private void parseStepLine(String header) {
-        String[] tokens = header.split(" ");
+        String[] tokens = header.split("\\s+");
         for (String token : tokens) {
             String[] keyValue = token.split("=");
             if (keyValue.length >= 2) {
@@ -289,7 +321,7 @@ public class WiggleParser {
                     // Per UCSC specification variable and fixed step coordinates are "1" based.
                     // We need to subtract 1 to convert to the internal "zero" based coordinates.
 
-                    start = Integer.parseInt(keyValue[1]) - 1;
+                    start = Integer.parseInt(keyValue[1]) - startBase;
                     if (start < lastPosition) {
                         unsortedChromosomes.add(chr);
                     }
@@ -298,43 +330,27 @@ public class WiggleParser {
                     step = Integer.parseInt(keyValue[1]);
                 } else if (keyValue[0].equalsIgnoreCase("span")) {
                     windowSpan = Integer.parseInt(keyValue[1]);
+                    updateLongestFeature(windowSpan);
                 }
 
             }
         }
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public double getMinValue() {
-        return minValue;
-    }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public double getMaxValue() {
-        return maxValue;
-    }
 
     private void changedChromosome(WiggleDataset dataset, String lastChr) {
-        int sz = 1000000;
 
-        Genome genome = IGVModel.getInstance().getViewContext().getGenome();
+        Genome genome = ViewContext.getInstance().getGenome();
         if (startLocations != null && startLocations.size() > 0) {
 
             String convertedChr = genome.getChromosomeAlias(lastChr);
             dataset.addDataChunk(convertedChr, startLocations, endLocations, data);
-            sz = startLocations.size();
+            //sz = startLocations.size();
         }
-        startLocations = new IntArrayList(sz);
-        endLocations = new IntArrayList(sz);
-        data = new FloatArrayList(sz);
-        lastPosition = 0;
+        startLocations = new IntArrayList(estArraySize);
+        endLocations = new IntArrayList(estArraySize);
+        data = new FloatArrayList(estArraySize);
+        lastPosition = -1;
     }
 }
diff --git a/src/org/broad/igv/data/ZipUtils.java b/src/org/broad/igv/data/ZipUtils.java
index d26c356..ce11751 100644
--- a/src/org/broad/igv/data/ZipUtils.java
+++ b/src/org/broad/igv/data/ZipUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/data/rnai/RNAIDataSource.java b/src/org/broad/igv/data/rnai/RNAIDataSource.java
index 2bde991..7e81498 100644
--- a/src/org/broad/igv/data/rnai/RNAIDataSource.java
+++ b/src/org/broad/igv/data/rnai/RNAIDataSource.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -21,14 +21,14 @@ package org.broad.igv.data.rnai;
 
 //~--- non-JDK imports --------------------------------------------------------
 
-import org.broad.igv.IGVConstants;
+import org.broad.igv.Globals;
 import org.broad.igv.data.DataSource;
 import org.broad.igv.feature.FeatureUtils;
 import org.broad.igv.feature.Genome;
 import org.broad.igv.feature.LocusScore;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.track.TrackType;
 import org.broad.igv.track.WindowFunction;
-import org.broad.igv.ui.IGVModel;
 
 import java.util.*;
 
@@ -79,7 +79,7 @@ public class RNAIDataSource implements DataSource {
      * @param dpt
      */
     public void addGeneScore(RNAIGeneScore dpt) {
-        String chr = dpt.getGene().getChromosome();
+        String chr = dpt.getGene().getChr();
         List<LocusScore> chrDataPoints = dataMap.get(chr);
         if (chrDataPoints == null) {
             chrDataPoints = new ArrayList(500);
@@ -88,15 +88,15 @@ public class RNAIDataSource implements DataSource {
         chrDataPoints.add(dpt);
 
         // Also add the score to the "All" chromosome.
-        List<LocusScore> chrAllScores = dataMap.get(IGVConstants.CHR_ALL);
+        List<LocusScore> chrAllScores = dataMap.get(Globals.CHR_ALL);
         if (chrAllScores == null) {
             chrAllScores = new ArrayList(500);
-            dataMap.put(IGVConstants.CHR_ALL, chrAllScores);
+            dataMap.put(Globals.CHR_ALL, chrAllScores);
         }
-        Genome genome = IGVModel.getInstance().getViewContext().getGenome();
+        Genome genome = ViewContext.getInstance().getGenome();
         RNAIGeneScore genomeScore = new RNAIGeneScore(dpt);
-        int genomeStart = genome.getGenomeCoordinate(dpt.getGene().getChromosome(), dpt.getStart());
-        int genomeEnd = genome.getGenomeCoordinate(dpt.getGene().getChromosome(), dpt.getEnd());
+        int genomeStart = genome.getGenomeCoordinate(dpt.getGene().getChr(), dpt.getStart());
+        int genomeEnd = genome.getGenomeCoordinate(dpt.getGene().getChr(), dpt.getEnd());
         genomeScore.setStart(genomeStart);
         genomeScore.setEnd(genomeEnd);
         chrAllScores.add(genomeScore);
@@ -113,40 +113,18 @@ public class RNAIDataSource implements DataSource {
 
     }
 
-    /**
-     * Method description
-     *
-     * @param chr
-     * @param windowFunction
-     * @return
-     */
-    public double getDataMax(String chr) {
+
+    public double getDataMax() {
         return 3;
     }
 
-    /**
-     * Method description
-     *
-     * @param chr
-     * @param windowFunction
-     * @return
-     */
-    public double getDataMin(String chr) {
+
+    public double getDataMin() {
         return -3;
     }
 
-    /**
-     * Method description
-     *
-     * @param chr
-     * @param startLocation
-     * @param endLocation
-     * @param zoom
-     * @param windowFunction
-     * @return
-     */
-    public List<LocusScore> getSummaryScoresForRange(String chr, int startLocation,
-                                                     int endLocation, int zoom) {
+
+    public List<LocusScore> getSummaryScoresForRange(String chr, int startLocation, int endLocation, int zoom) {
 
         if (!scoresAreSorted) {
             sortScores();
@@ -211,7 +189,7 @@ public class RNAIDataSource implements DataSource {
         return condition;
     }
 
-    public String getDisplayName() {
+    public String getName() {
         return displayName;
     }
 
diff --git a/src/org/broad/igv/data/rnai/RNAIGCTDatasetParser.java b/src/org/broad/igv/data/rnai/RNAIGCTDatasetParser.java
index 44af76b..678d462 100644
--- a/src/org/broad/igv/data/rnai/RNAIGCTDatasetParser.java
+++ b/src/org/broad/igv/data/rnai/RNAIGCTDatasetParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -24,8 +24,9 @@ import org.broad.igv.feature.Feature;
 import org.broad.igv.feature.FeatureDB;
 import org.broad.igv.feature.GeneManager;
 import org.broad.igv.feature.ProbeToGeneMap;
-import org.broad.igv.ui.IGVModel;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.util.AsciiLineReader;
+import org.broad.igv.util.IGVHttpUtils;
 import org.broad.igv.util.ParsingUtils;
 import org.broad.igv.util.ResourceLocator;
 
@@ -60,7 +61,7 @@ public class RNAIGCTDatasetParser {
 
         dataStartColumn = 2;
 
-        String genome = IGVModel.getInstance().getViewContext().getGenomeId();
+        String genome = ViewContext.getInstance().getGenomeId();
 
         this.geneManager = GeneManager.getGeneManager(genome);
 
@@ -71,6 +72,7 @@ public class RNAIGCTDatasetParser {
         AsciiLineReader reader = null;
         List dataSources = null;
         String nextLine = null;
+         InputStream probeMappingStream = null;
 
         try {
             String[] tokens = new String[1000];
@@ -102,8 +104,8 @@ public class RNAIGCTDatasetParser {
 
             Map<String, String[]> rnaiProbeMap = ProbeToGeneMap.getInstance().getRNAiProbeMap();
             URL url = new URL(RNAI_MAPPING_FILE);
-            URLConnection connection = url.openConnection();
-            InputStream probeMappingStream = new GZIPInputStream(connection.getInputStream());
+
+            probeMappingStream = new GZIPInputStream(IGVHttpUtils.openConnectionStream(url));
             if (probeMappingStream == null) {
                 log.error("Could not retrieve probe mapping file: " + RNAI_MAPPING_FILE);
                 return null;
@@ -194,6 +196,13 @@ public class RNAIGCTDatasetParser {
             log.error("Error parsing RNAi file", ex);
             throw new RuntimeException(ex);
         } finally {
+            if(probeMappingStream != null) {
+                try {
+                    probeMappingStream.close();
+                } catch (IOException e) {
+                    log.error("Error closing probe mapping stream", e);
+                }
+            }
             if (reader != null) {
                 reader.close();
             }
diff --git a/src/org/broad/igv/data/rnai/RNAIGeneScore.java b/src/org/broad/igv/data/rnai/RNAIGeneScore.java
index 080aa49..48de36c 100644
--- a/src/org/broad/igv/data/rnai/RNAIGeneScore.java
+++ b/src/org/broad/igv/data/rnai/RNAIGeneScore.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -130,6 +130,10 @@ public class RNAIGeneScore implements LocusScore {
         return gene;
     }
 
+    public String getChr() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
     /**
      * Method description
      *
diff --git a/src/org/broad/igv/data/rnai/RNAIGeneScoreParser.java b/src/org/broad/igv/data/rnai/RNAIGeneScoreParser.java
index 079898d..fa953e5 100644
--- a/src/org/broad/igv/data/rnai/RNAIGeneScoreParser.java
+++ b/src/org/broad/igv/data/rnai/RNAIGeneScoreParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/data/rnai/RNAIHairpinCache.java b/src/org/broad/igv/data/rnai/RNAIHairpinCache.java
index 9024dfe..b9939c5 100644
--- a/src/org/broad/igv/data/rnai/RNAIHairpinCache.java
+++ b/src/org/broad/igv/data/rnai/RNAIHairpinCache.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/data/rnai/RNAIHairpinParser.java b/src/org/broad/igv/data/rnai/RNAIHairpinParser.java
index af31154..59d638d 100644
--- a/src/org/broad/igv/data/rnai/RNAIHairpinParser.java
+++ b/src/org/broad/igv/data/rnai/RNAIHairpinParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -20,8 +20,8 @@ package org.broad.igv.data.rnai;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
-import org.broad.igv.util.ParsingUtils;
 import org.broad.igv.util.AsciiLineReader;
+import org.broad.igv.util.ParsingUtils;
 
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
diff --git a/src/org/broad/igv/data/rnai/RNAIHairpinValue.java b/src/org/broad/igv/data/rnai/RNAIHairpinValue.java
index e69e366..d78aca6 100644
--- a/src/org/broad/igv/data/rnai/RNAIHairpinValue.java
+++ b/src/org/broad/igv/data/rnai/RNAIHairpinValue.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/data/seg/GBenchFileParser.java b/src/org/broad/igv/data/seg/GBenchFileParser.java
new file mode 100644
index 0000000..82729c3
--- /dev/null
+++ b/src/org/broad/igv/data/seg/GBenchFileParser.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+package org.broad.igv.data.seg;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import org.apache.log4j.Logger;
+import org.broad.igv.exceptions.DataLoadException;
+import org.broad.igv.exceptions.ParserException;
+import org.broad.igv.feature.Genome;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.synteny.SyntenyMapping;
+import org.broad.igv.synteny.SyntenyUtils;
+import org.broad.igv.track.TrackType;
+import org.broad.igv.util.AsciiLineReader;
+import org.broad.igv.util.ParsingUtils;
+import org.broad.igv.util.ResourceLocator;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Enter your name here...
+ * @version Enter version here..., 09/01/09
+ */
+public class GBenchFileParser implements SegFileParser {
+
+    private static Logger log = Logger.getLogger(GBenchFileParser.class);
+    String testMappings = "test/data/hg18_to_mm8.regions";
+
+    int chrColumn = 1;
+    int startColumn = 3;
+    int endColumn = 4;
+    int snpCountColumn = 5;
+    int ampColumn = 6;
+    int delColumn = 7;
+    ResourceLocator locator;
+    Map<String, List<SyntenyMapping>> mappings;
+
+    /**
+     * Constructs ...
+     *
+     * @param locator
+     */
+    public GBenchFileParser(ResourceLocator locator) {
+        this.locator = locator;
+
+
+    }
+
+    /**
+     * Method description
+     *
+     * @param dataset
+     */
+    public void loadSegmentTracks(SegmentedAsciiDataSet dataset) {
+        loadSegments(dataset);
+
+    }
+
+    /**
+     * Return a map of trackId -> segment datasource
+     *
+     * @return
+     */
+    public void loadSegments(SegmentedAsciiDataSet dataset) {
+        AsciiLineReader reader = null;
+        String nextLine = null;
+        try {
+            reader = ParsingUtils.openAsciiReader(locator);
+
+            // Parse comments, if any
+            nextLine = reader.readLine();
+            while (nextLine.startsWith("#") || (nextLine.trim().length() == 0)) {
+                if (nextLine.length() > 0) {
+                    parseComment(nextLine, dataset);
+                }
+                nextLine = reader.readLine();
+            }
+
+            // Read column headings
+            String[] headings = null;
+
+            while ((nextLine = reader.readLine()) != null) {
+                if (nextLine.startsWith("AberrationNo")) {
+                    headings = nextLine.split("\t");
+                    break;
+                }
+            }
+            if (headings == null) {
+                throw new DataLoadException("Error: Cannot find data section.", locator.toString());
+            }
+
+
+            Genome genome = ViewContext.getInstance().getGenome();
+
+            //
+            //if (genome.getId().equals("hg18")) {
+            //    mappings = SyntenyUtils.loadMappings(testMappings, true);
+            //}
+
+            String trackId = "";
+            while ((nextLine = reader.readLine()) != null && (nextLine.trim().length() > 0)) {
+
+                String[] tokens = nextLine.split("\t");
+                int nTokens = tokens.length;
+                if (nTokens == 1) {
+                    trackId = tokens[0];
+                } else if (nTokens > 4) {
+                    String chr = (genome == null ? tokens[chrColumn].trim() :
+                            genome.getChromosomeAlias(tokens[chrColumn].trim()));
+                    int start;
+                    int end;
+                    try {
+                        start = Integer.parseInt(tokens[startColumn].trim());
+                    }
+                    catch (NumberFormatException numberFormatException) {
+                        throw new ParserException("Column " + (startColumn + 1) + " must contain a numeric value.",
+                                reader.getCurrentLineNumber(), nextLine);
+                    }
+                    try {
+                        end = Integer.parseInt(tokens[endColumn].trim());
+                    }
+                    catch (NumberFormatException numberFormatException) {
+                        throw new ParserException("Column " + (endColumn + 1) + " must contain a numeric value.",
+                                reader.getCurrentLineNumber(), nextLine);
+                    }
+
+                    int snpCount = 0;
+                    if (snpCountColumn > 0) {
+                        try {
+                            snpCount = Integer.parseInt(tokens[snpCountColumn]);
+                        } catch (NumberFormatException numberFormatException) {
+
+                            // This is an expected condition, nothing needs done.
+                        }
+                    }
+
+
+                    float value = Float.NaN;
+                    try {
+                        value = Float.parseFloat(tokens[ampColumn]);
+                        if (value == 0) {
+                            value = Float.parseFloat(tokens[delColumn]);
+                        }
+
+                    } catch (NumberFormatException numberFormatException) {
+
+
+                    }
+
+                    if (mappings != null) {
+                        List<SyntenyMapping> tmp = mappings.get(chr);
+                        if (tmp == null) {
+                            System.out.println("No mappings for chr: " + chr);
+                            continue;
+                        }
+                        if(chr.equals("chr1")) {
+                            System.out.println();
+                        }
+                        List<SyntenyMapping> overlappingMappings = SyntenyUtils.getMappingsOverlapping(tmp, start, end);
+                        if(overlappingMappings == null) {
+                            System.out.println("No mapping for: " + chr + ":" + start + "-" + end);
+                            continue;
+                        }
+                        if (overlappingMappings.size() == 1) {
+                            SyntenyMapping m1 = overlappingMappings.get(0);
+                            int p1 = (int) m1.mapPosition(start);
+                            int p2 = (int) m1.mapPosition(end);
+                            start = Math.min(p1, p2);
+                            end = Math.max(p1, p2);
+                            dataset.addSegment(trackId, m1.getToChr(), start, end, value, snpCount);
+                        } else {
+                            SyntenyMapping m1 = overlappingMappings.get(0);
+                            String chr1 = m1.getToChr();
+                            int s1 = (int) m1.mapPosition(start);
+                            int e1 = m1.getToEnd();
+                            if (!m1.getDirection()) {
+                                s1 = m1.getToStart();
+                                e1 = (int) m1.mapPosition(start);
+                            }
+                            dataset.addSegment(trackId, chr1, s1, e1, value, snpCount);
+
+                            SyntenyMapping m2 = overlappingMappings.get(overlappingMappings.size() - 1);
+                            String chr2 = m2.getToChr();
+                            int s2 = (int) m1.mapPosition(end);
+                            int e2 = m1.getToEnd();
+                            if (!m1.getDirection()) {
+                                s2 = m1.getToStart();
+                                e2 = (int) m1.mapPosition(end);
+                            }
+                            dataset.addSegment(trackId, chr2, s2, e2, value, snpCount);
+
+                            for (int i = 1; i < overlappingMappings.size(); i++) {
+                                SyntenyMapping m = overlappingMappings.get(i);
+                                dataset.addSegment(trackId, m.getToChr(), m.getToStart(), m.getToEnd(), value, snpCount);
+                            }
+                        }
+
+                    } else {
+                        dataset.addSegment(trackId, chr, start, end, value, snpCount);
+                    }
+                }
+            }
+
+
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+            if (nextLine != null && reader.getCurrentLineNumber() != 0) {
+                throw new ParserException(e.getMessage(), e, reader.getCurrentLineNumber(), nextLine);
+            } else {
+                throw new RuntimeException(e);
+            }
+        } finally {
+            if (reader == null) {
+                reader.close();
+            }
+        }
+    }
+
+
+    /**
+     * Note:  This is an exact copy of the method in GCTDatasetParser.  Refactor to merge these
+     * two parsers, or share a common base class.
+     *
+     * @param comment
+     * @param dataset
+     */
+    private void parseComment(String comment, SegmentedAsciiDataSet dataset) {
+
+        String tmp = comment.substring(1, comment.length());
+        String[] tokens = tmp.split("=");
+        if (tokens.length != 2) {
+            return;
+        }
+        String key = tokens[0].trim().toLowerCase();
+        if (key.equals("type")) {
+
+            try {
+                dataset.setTrackType(TrackType.valueOf(tokens[1].trim().toUpperCase()));
+            } catch (Exception exception) {
+
+                // Ignore
+
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/org/broad/igv/data/seg/SegFileParser.java b/src/org/broad/igv/data/seg/SegFileParser.java
new file mode 100644
index 0000000..17b732f
--- /dev/null
+++ b/src/org/broad/igv/data/seg/SegFileParser.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.data.seg;
+
+/**
+ * User: jrobinso
+ * Date: Feb 18, 2010
+ */
+public interface SegFileParser {
+    void loadSegments(SegmentedAsciiDataSet dataset);
+}
diff --git a/src/org/broad/igv/data/seg/Segment.java b/src/org/broad/igv/data/seg/Segment.java
new file mode 100644
index 0000000..0b84b6f
--- /dev/null
+++ b/src/org/broad/igv/data/seg/Segment.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.data.seg;
+
+import org.broad.igv.feature.LocusScore;
+import org.broad.igv.track.WindowFunction;
+
+/**
+ * @author Enter your name here...
+ * @version Enter version here..., 09/01/09
+ */
+public class Segment implements LocusScore {
+
+    private int extendedStart = -1;
+    private int extendedEnd = -1;
+    private int start;
+    private int end;
+    private float score;
+    private int snpCount;
+
+    public Segment(int start, int end, float score) {
+        this(start, end, score, 0);
+    }
+
+    public Segment(int start, int end, float score, int snpCount) {
+        this.start = start;
+        this.end = end;
+        if (extendedStart < 0) {
+            extendedStart = start;
+        }
+        if (extendedEnd < 0) {
+            extendedEnd = end;
+        }
+        this.score = score;
+        this.snpCount = snpCount;
+    }
+
+
+    //Segment(int start, int end,  float score, int snpCount) {
+    //    this(start, start, end, end, score, snpCount);
+    //}
+    public Segment(int start, int origStart, int end, int origEnd, float score, int snpCount) {
+        this.start = start;
+        this.end = end;
+        this.extendedStart = origStart;
+        this.extendedEnd = origEnd;
+        this.score = score;
+        this.snpCount = snpCount;
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public Segment copy() {
+        return new Segment(start, extendedStart, end, extendedEnd, score, snpCount);
+    }
+
+    public String getChr() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public int getStart() {
+        return start;
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public int getEnd() {
+        return end;
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public float getScore() {
+        return score;
+    }
+
+    /**
+     * Method description
+     *
+     * @param start
+     */
+    public void setStart(int start) {
+        this.start = start;
+    }
+
+    /**
+     * Method description
+     *
+     * @param end
+     */
+    public void setEnd(int end) {
+        this.end = end;
+    }
+
+    /**
+     * Method description
+     *
+     * @param confidence
+     */
+    public void setConfidence(float confidence) {
+
+        // Ignored for now
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public float getConfidence() {
+        return 1.0f;
+    }
+
+    /**
+     * Method description
+     *
+     * @param position
+     * @param ignored
+     * @return
+     */
+    public String getValueString(double position, WindowFunction ignored) {
+        String valueString = "Copy number: " + getScore();
+        if (snpCount > 0) {
+            valueString += " (" + snpCount + " markers)";
+        }
+        return valueString;
+    }
+
+    /**
+     * Method description
+     *
+     * @param inc
+     */
+    public void incremenetSnpCount(int inc) {
+        snpCount += inc;
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public int getSnpCount() {
+        return snpCount;
+    }
+
+    /**
+     * @return the extendedStart
+     */
+    public int getExtendedStart() {
+        return extendedStart;
+    }
+
+    /**
+     * @return the extendedEnd
+     */
+    public int getExtendedEnd() {
+        return extendedEnd;
+    }
+
+    /**
+     * @param extendedStart the extendedStart to set
+     */
+    public void setExtendedStart(int extendedStart) {
+        this.extendedStart = extendedStart;
+    }
+
+    /**
+     * @param extendedEnd the extendedEnd to set
+     */
+    public void setExtendedEnd(int extendedEnd) {
+        this.extendedEnd = extendedEnd;
+    }
+}
diff --git a/src/org/broad/igv/data/seg/SegmentFileParser.java b/src/org/broad/igv/data/seg/SegmentFileParser.java
new file mode 100644
index 0000000..1ada402
--- /dev/null
+++ b/src/org/broad/igv/data/seg/SegmentFileParser.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+package org.broad.igv.data.seg;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import org.apache.log4j.Logger;
+import org.broad.igv.exceptions.DataLoadException;
+import org.broad.igv.exceptions.ParserException;
+import org.broad.igv.feature.Genome;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.track.TrackProperties;
+import org.broad.igv.track.TrackType;
+import org.broad.igv.util.AsciiLineReader;
+import org.broad.igv.util.ParsingUtils;
+import org.broad.igv.util.ResourceLocator;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Enter your name here...
+ * @version Enter version here..., 09/01/09
+ */
+public class SegmentFileParser implements SegFileParser {
+
+    enum Type {
+        SEG, BIRDSUITE, NEXUS
+    }
+
+    ;
+
+    private static Logger log = Logger.getLogger(SegmentFileParser.class);
+
+    boolean birdsuite = false;
+    int sampleColumn = 0;
+    int chrColumn = 1;
+    int startColumn = 2;
+    int endColumn = 3;
+    int snpCountColumn = 4;    // Default value
+    int dataColumn = 5;        // Default value
+    ResourceLocator locator;
+
+    /**
+     * Constructs ...
+     *
+     * @param locator
+     */
+    public SegmentFileParser(ResourceLocator locator) {
+        this.locator = locator;
+        if (locator.getPath().toLowerCase().endsWith("birdseye_canary_calls")) {
+            birdsuite = true;
+        }
+    }
+
+
+    /**
+     * Return a map of trackId -> segment datasource
+     *
+     * @return
+     */
+    public void loadSegments(SegmentedAsciiDataSet dataset) {
+
+        if (birdsuite) {
+            dataset.setTrackType(TrackType.CNV);
+        }
+
+        AsciiLineReader reader = null;
+        String nextLine = null;
+        try {
+            reader = ParsingUtils.openAsciiReader(locator);
+
+            // Parse comments, if any
+            nextLine = reader.readLine();
+            while (nextLine.startsWith("#") || (nextLine.trim().length() == 0)) {
+                if (nextLine.length() > 0) {
+                    parseComment(nextLine, dataset);
+                }
+                nextLine = reader.readLine();
+            }
+
+            // Read column headings
+            String[] headings = nextLine.split("\t");
+
+
+            if (birdsuite) {
+                //sample	sample_index	copy_number	chr	start	end	confidence
+                sampleColumn = 0;
+                chrColumn = 3;
+                startColumn = 4;
+                endColumn = 5;
+                snpCountColumn = 6;
+                dataColumn = 2;
+            } else {
+                // The data value is always the last column
+                dataColumn = headings.length - 1;
+                // We assume the snp count is next to last, but not 0-3
+                snpCountColumn = dataColumn - 1;
+                if (snpCountColumn < 4) {
+                    snpCountColumn = -1;
+                }
+            }
+
+            String[] tokens = new String[headings.length];
+
+            Genome genome = ViewContext.getInstance().getGenome();
+
+            while ((nextLine = reader.readLine()) != null && (nextLine.trim().length() > 0)) {
+
+                int nTokens = ParsingUtils.split(nextLine, tokens, '\t');
+                if (nTokens > 4) {
+
+                    int start;
+                    int end;
+                    try {
+                        start = Integer.parseInt(tokens[startColumn].trim());
+                    }
+                    catch (NumberFormatException numberFormatException) {
+                        throw new ParserException("Column " + (startColumn + 1) + " must contain a numeric value.",
+                                reader.getCurrentLineNumber(), nextLine);
+                    }
+                    try {
+                        end = Integer.parseInt(tokens[endColumn].trim());
+                    }
+                    catch (NumberFormatException numberFormatException) {
+                        throw new ParserException("Column " + (endColumn + 1) + " must contain a numeric value.",
+                                reader.getCurrentLineNumber(), nextLine);
+                    }
+
+                    String chr = tokens[chrColumn].trim();
+                    if (genome != null) {
+                        chr = genome.getChromosomeAlias(chr);
+                    }
+
+
+                    String trackId = new String(tokens[sampleColumn].trim());
+
+                    int snpCount = 0;
+                    if (snpCountColumn > 0) {
+                        try {
+                            snpCount = Integer.parseInt(tokens[snpCountColumn]);
+                        } catch (NumberFormatException numberFormatException) {
+
+                            // This is an expected condition, nothing needs done.
+                        }
+                    }
+
+                    try {
+                        float value = Float.parseFloat(tokens[dataColumn]);
+                        dataset.addSegment(trackId, chr, start, end, value, snpCount);
+                    } catch (NumberFormatException numberFormatException) {
+                        log.info("Skipping line: " + nextLine);
+                    }
+                }
+            }
+
+        }
+        catch (DataLoadException pe) {
+            throw pe;
+        }
+        catch (ParserException pe) {
+            throw pe;
+        }
+        catch (Exception e) {
+            if (nextLine != null && reader.getCurrentLineNumber() != 0) {
+                throw new ParserException(e.getMessage(), e, reader.getCurrentLineNumber(), nextLine);
+            } else {
+                throw new RuntimeException(e);
+            }
+        } finally {
+            if (reader == null) {
+                reader.close();
+            }
+        }
+    }
+
+
+    /**
+     * Note:  This is an exact copy of the method in GCTDatasetParser.  Refactor to merge these
+     * two parsers, or share a common base class.
+     *
+     * @param comment
+     * @param dataset
+     */
+    private void parseComment(String comment, SegmentedAsciiDataSet dataset) {
+
+        String tmp = comment.substring(1, comment.length());
+        if (tmp.startsWith("type")) {
+            String[] tokens = tmp.split("=");
+            if (tokens.length != 2) {
+                return;
+            }
+            try {
+                dataset.setTrackType(TrackType.valueOf(tokens[1].trim().toUpperCase()));
+            } catch (Exception exception) {
+
+            }
+        } else if (tmp.startsWith("track")) {
+            TrackProperties props = new TrackProperties();
+            ParsingUtils.parseTrackLine(comment, props);
+            dataset.setTrackProperties(props);
+        }
+    }
+}
diff --git a/src/org/broad/igv/data/seg/SegmentedAsciiDataSet.java b/src/org/broad/igv/data/seg/SegmentedAsciiDataSet.java
new file mode 100644
index 0000000..d7f3a68
--- /dev/null
+++ b/src/org/broad/igv/data/seg/SegmentedAsciiDataSet.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * sample       chrom   loc.start       loc.end num.mark        num.informative seg.mean
+TCGA-02-0001-01C-01D-0183-04    1       554268  74674720        6892    6721    0.2077
+TCGA-02-0001-01C-01D-0183-04    1       74693652        75110251        37      37      -0.2659
+ */
+package org.broad.igv.data.seg;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import org.broad.igv.feature.FeatureUtils;
+import org.broad.igv.feature.Genome;
+import org.broad.igv.feature.LocusScore;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.synteny.SyntenyMapping;
+import org.broad.igv.synteny.SyntenyUtils;
+import org.broad.igv.track.TrackProperties;
+import org.broad.igv.track.TrackType;
+import org.broad.igv.util.ResourceLocator;
+
+import java.util.*;
+
+
+/**
+ * @author jrobinso
+ */
+public class SegmentedAsciiDataSet implements SegmentedDataSet {
+
+    SegFileParser parser;
+    TrackType trackType = TrackType.COPY_NUMBER;
+    float dataMax = -Float.MAX_VALUE;
+    float dataMin = Float.MAX_VALUE;
+    /**
+     * Assume data is non-log value until suggested otherwise by the precense
+     * of negative numbers.  TODO This is a fragile assumption, the user should
+     * input this information directly.
+     */
+    private boolean logNormalized = false;
+    /**
+     * Map of [heading ->  [chr -> [list of chrSegments]]]
+     */
+    private Map<String, Map<String, List<LocusScore>>> segments = new HashMap();
+    /**
+     * Set of chromosomes represented in this dataset
+     */
+    private Set<String> chromosomes = new HashSet();
+    private List<String> headings = new ArrayList();
+    private Map<String, List<LocusScore>> wholeGenomeScoresCache = new HashMap();
+    private long lastRefreshTime = 0;
+    private TrackProperties trackProperties;
+
+    /**
+     * Constructs ...
+     *
+     * @param locator
+     */
+    public SegmentedAsciiDataSet(ResourceLocator locator) {
+        parser = locator.getPath().toLowerCase().endsWith(".gbench") ?
+                new GBenchFileParser(locator) :
+                new SegmentFileParser(locator);
+        parser.loadSegments(this);
+        sortLists();
+
+
+    }
+
+
+    public void sortLists() {
+        for (Map.Entry<String, Map<String, List<LocusScore>>> sampleEntry : segments.entrySet()) {
+            for (Map.Entry<String, List<LocusScore>> chrEntry : sampleEntry.getValue().entrySet()) {
+                List<LocusScore> tmp = chrEntry.getValue();
+                FeatureUtils.sortFeatureList(tmp);
+            }
+        }
+    }
+
+    /**
+     * Method description
+     *
+     * @param timestamp
+     */
+    public synchronized void refreshData(long timestamp) {
+        if (timestamp > lastRefreshTime) {
+            dataMax = -Float.MAX_VALUE;
+            dataMin = Float.MAX_VALUE;
+            logNormalized = false;
+            segments = new HashMap();
+            headings = new ArrayList();
+            wholeGenomeScoresCache.clear();
+            parser.loadSegments(this);
+            lastRefreshTime = timestamp;
+        }
+
+    }
+
+    /**
+     *
+     */
+    public void addSegment(String heading, String chr, int start, int end, float value, int snpCount) {
+
+        Map<String, List<LocusScore>> chrSegments = segments.get(heading);
+        if (chrSegments == null) {
+            headings.add(heading);
+            chrSegments = new HashMap();
+            segments.put(heading, chrSegments);
+        }
+
+        List<LocusScore> segmentList = chrSegments.get(chr);
+        if (segmentList == null) {
+            segmentList = new ArrayList<LocusScore>();
+            chrSegments.put(chr, segmentList);
+        }
+        segmentList.add(new Segment(start, start, end, end, value, snpCount));
+        dataMax = Math.max(dataMax, value);
+        dataMin = Math.min(dataMin, value);
+        if (value < 0) {
+            logNormalized = true;
+        }
+
+        chromosomes.add(chr);
+
+    }
+
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public Set<String> getChromosomes() {
+        return chromosomes;
+    }
+
+    /**
+     * Method description
+     *
+     * @param heading
+     * @param chr
+     * @return
+     */
+    public List<LocusScore> getSegments(String heading, String chr) {
+        Map<String, List<LocusScore>> chrSegments = segments.get(heading);
+        return (chrSegments == null) ? null : chrSegments.get(chr);
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public List<String> getDataHeadings() {
+        return headings;
+    }
+
+
+    public List<String> getSampleNames() {
+        return headings;
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public TrackType getType() {
+        return trackType;
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public boolean isLogNormalized() {
+        return logNormalized;
+    }
+
+    /**
+     * Method description
+     *
+     * @param chr
+     * @return
+     */
+    public double getDataMax(String chr) {
+        return dataMax;
+    }
+
+    /**
+     * Method description
+     *
+     * @param chr
+     * @return
+     */
+    public double getDataMin(String chr) {
+        return dataMin;
+    }
+
+    /**
+     * Method description
+     *
+     * @param heading
+     * @return
+     */
+    public List<LocusScore> getWholeGenomeScores(String heading) {
+
+
+        List<LocusScore> wholeGenomeScores = wholeGenomeScoresCache.get(heading);
+        if ((wholeGenomeScores == null) || wholeGenomeScores.isEmpty()) {
+            int locationUnit = 1000;
+            Genome genome = ViewContext.getInstance().getGenome();
+
+            // Compute the smallest concievable feature that could be viewed on the
+            // largest screen.  Be conservative.   The smallest feature is one at
+            // the screen resolution scale in <chr units> / <pixel>
+            int maxScreenSize = 4000;
+            double minFeatureSize = 0; // ((double) genome.getLength()) / (maxScreenSize * locationUnit);
+
+            long offset = 0;
+            wholeGenomeScores = new ArrayList(1000);
+            for (String chr : genome.getChromosomeNames()) {
+                List<LocusScore> chrSegments = getSegments(heading, chr);
+                if (chrSegments != null) {
+                    int lastgEnd = -1;
+                    for (LocusScore score : chrSegments) {
+                        Segment seg = (Segment) score;
+                        int gStart = (int) ((offset + score.getStart()) / locationUnit);
+                        int gEnd = (int) ((offset + score.getEnd()) / locationUnit);
+                        if ((gEnd - gStart) > minFeatureSize) {
+                            wholeGenomeScores.add(new Segment(gStart, gStart, gEnd,
+                                    gEnd, seg.getScore(), seg.getSnpCount()));
+                        }
+                    }
+
+                }
+                offset += genome.getChromosome(chr).getLength();
+            }
+            wholeGenomeScoresCache.put(heading, wholeGenomeScores);
+        }
+        return wholeGenomeScores;
+
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public TrackType getTrackType() {
+        return trackType;
+    }
+
+    /**
+     * Method description
+     *
+     * @param trackType
+     */
+    public void setTrackType(TrackType trackType) {
+        this.trackType = trackType;
+    }
+
+
+    public void setTrackProperties(TrackProperties props) {
+        this.trackProperties = props;
+    }
+
+    public TrackProperties getTrackProperties() {
+        return trackProperties;
+    }
+}
diff --git a/src/org/broad/igv/data/seg/SegmentedBinaryDataSet.java b/src/org/broad/igv/data/seg/SegmentedBinaryDataSet.java
new file mode 100644
index 0000000..6507d58
--- /dev/null
+++ b/src/org/broad/igv/data/seg/SegmentedBinaryDataSet.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.data.seg;
+
+import org.broad.igv.Globals;
+import org.broad.igv.feature.LocusScore;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.track.TrackType;
+import org.broad.igv.util.ResourceLocator;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author jrobinso
+ */
+public class SegmentedBinaryDataSet implements SegmentedDataSet {
+
+    SegmentedBinaryReader reader;
+
+    private long lastRefreshTime = 0;
+
+    /**
+     * Assume data is non-log value until suggested otherwise by the precense
+     * of negative numbers.  TODO This is a fragile assumption, the user should
+     * input this information directly.
+     */
+    Boolean logNormalized = null;
+
+    private List<String> sampleNames = null;
+
+    /**
+     * Map of [heading ->  [chr -> [list of segmentsCache]]]
+     */
+    Map<String, Map<String, List<LocusScore>>> segmentsCache = new HashMap();
+
+    Map<String, SegmentedChromosomeData> chrData = new HashMap();
+
+    TrackType type = TrackType.COPY_NUMBER;
+
+    public SegmentedBinaryDataSet(ResourceLocator locator) {
+
+        reader = locator.getServerURL() == null ? new SegmentedBinaryLocalReader(locator.getPath()) : new SegmentedBinaryRemoteReader(locator);
+
+        try {
+            type = TrackType.valueOf(reader.getStringAttribute("type"));
+        }
+        catch (Exception e) {
+            //ignore;
+        }
+
+        try {
+            String logNormalizedString = reader.getStringAttribute("logNormalized");
+            if (logNormalizedString != null) {
+                logNormalized = logNormalizedString.equals("true");
+            }
+
+        } catch (Exception exception) {
+            // ignore
+        }
+
+        // TODO Temporary -- start loading whole genome scores ascynchonously
+        if (!ViewContext.getInstance().getChrName().equals(Globals.CHR_ALL)) {
+            (new Thread(new Runnable() {
+
+                public void run() {
+                    synchronized (chrData) {
+                        SegmentedChromosomeData cd = reader.getChromosomeData(Globals.CHR_ALL);
+                        chrData.put(Globals.CHR_ALL, cd);
+                    }
+                }
+            })).start();
+        }
+
+    }
+
+    public synchronized void refreshData(long timestamp) {
+        if (timestamp > lastRefreshTime) {
+            logNormalized = false;
+            segmentsCache = new HashMap();
+            lastRefreshTime = timestamp;
+        }
+
+    }
+
+    // TODO -- the synchronized keyword can be removed with the async whole genome load
+
+    public List<LocusScore> getSegments(String heading, String chr) {
+
+        Map<String, List<LocusScore>> chrSegments = segmentsCache.get(heading);
+        if (chrSegments == null) {
+            chrSegments = new HashMap();
+            segmentsCache.put(heading, chrSegments);
+        }
+
+        List<LocusScore> segments = chrSegments.get(chr);
+
+        if (segments == null) {
+
+            SegmentedChromosomeData cd = chrData.get(chr);
+            if (cd == null) {
+                cd = reader.getChromosomeData(chr);
+                chrData.put(chr, cd);
+            }
+
+            int[] startLocations = cd.getStartLocations(heading);
+            int[] endLocations = cd.getEndLocations(heading);
+            float[] values = cd.getValues(heading);
+
+            if (startLocations == null || startLocations.length == 0) {
+                return null;
+            }
+
+            if (logNormalized == null) {
+                logNormalized = false;
+                for (int i = 0; i <
+                        values.length; i++) {
+                    if (values[i] < 0) {
+                        logNormalized = true;
+                        break;
+                    }
+
+                }
+            }
+            assert (startLocations.length == endLocations.length);
+            assert (endLocations.length == values.length);
+
+            segments = new ArrayList(startLocations.length);
+            for (int i = 0; i <
+                    startLocations.length; i++) {
+                segments.add(new Segment(startLocations[i], endLocations[i], values[i]));
+                chrSegments.put(chr, segments);
+            }
+
+        }
+        return segments;
+    }
+
+    public List<String> getSampleNames() {
+        if (sampleNames == null) {
+            sampleNames = reader.getSampleNames();
+        }
+
+        return sampleNames;
+    }
+
+    public TrackType getType() {
+        return type;
+    }
+
+    // TODO -- check for negative numbers
+
+    public boolean isLogNormalized() {
+        return logNormalized == null ? true : logNormalized;
+    }
+
+    public double getDataMax(String chr) {
+        return 3;
+    }
+
+    public double getDataMin(String chr) {
+        return -3;
+    }
+
+    public synchronized List<LocusScore> getWholeGenomeScores(String heading) {
+        return getSegments(heading, Globals.CHR_ALL);
+
+    }
+
+    public static class ChromosomeChunk {
+    }
+
+}
diff --git a/src/org/broad/igv/data/seg/SegmentedBinaryLocalReader.java b/src/org/broad/igv/data/seg/SegmentedBinaryLocalReader.java
new file mode 100644
index 0000000..7c7af54
--- /dev/null
+++ b/src/org/broad/igv/data/seg/SegmentedBinaryLocalReader.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.data.seg;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+
+/**
+ * @author jrobinso
+ */
+public class SegmentedBinaryLocalReader implements SegmentedBinaryReader {
+
+    String filePath;
+
+    List<String> chromosomes;
+
+    List<String> sampleNames;
+
+    Map<String, String> dataFileMap = new HashMap();
+
+    Map<String, String> attrs;
+
+    public SegmentedBinaryLocalReader(String filePath) {
+        this.filePath = filePath;
+    }
+
+    /**
+     * Return the set of sample names in this data set
+     *
+     * @return
+     */
+    public List<String> getSampleNames() {
+        if (sampleNames == null) {
+            sampleNames = readAsStrings("data/samples.txt");
+        }
+        return sampleNames;
+
+    }
+
+    public String getStringAttribute(String key) {
+        return getAttributes().get(key);
+    }
+
+    public Map<String, String> getAttributes() {
+        if (attrs == null) {
+            attrs = new HashMap();
+            List<String> lines = readAsStrings("data/attributes.txt");
+            if (lines != null) {
+                for (String kv : lines) {
+                    String[] tokens = kv.split("=");
+                    attrs.put(tokens[0], tokens[1]);
+                }
+            }
+        }
+        return attrs;
+
+    }
+
+    public SegmentedChromosomeData getChromosomeData(String chr) {
+
+        return readChromosomeData(chr);
+
+    }
+
+    /**
+     * Return the segment start locations for the sample and chromosome
+     *
+     * @param sample the sample name
+     * @param chr    the chromosome name
+     * @return array of end locations
+     */
+    public int[] getStartLocations(String sample, String chr) {
+        String entryName = "data/" + sample + "/" + chr + "/starts.bin";
+        return readAsInts(entryName);
+    }
+
+    /**
+     * Return the segment end locations for the sample and chromosome
+     *
+     * @param sample the sample name
+     * @param chr    the chromosome name
+     * @return array of end locations
+     */
+    public int[] getEndLocations(String sample, String chr) {
+        String entryName = "data/" + sample + "/" + chr + "/ends.bin";
+        return readAsInts(entryName);
+    }
+
+    /**
+     * Return the data values for the given sample and chromosom
+     *
+     * @param sample the sample name
+     * @param chr    the chromosome name
+     * @return data values
+     */
+    public float[] getValues(String sample, String chr) {
+        String entryName = "data/" + sample + "/" + chr + "/values.bin";
+        return readAsFloats(entryName);
+    }
+
+    private List<String> readAsStrings(String entryName) {
+        List<String> strings = new ArrayList();
+        ZipFile zipFile = null;
+        try {
+            zipFile = new ZipFile(new File(filePath));
+
+            ZipEntry entry = zipFile.getEntry(entryName);
+            if (entry == null) {
+                return null;
+            }
+
+            BufferedReader br = new BufferedReader(new InputStreamReader(zipFile.getInputStream(entry)));
+            String nextLine = null;
+            while ((nextLine = br.readLine()) != null) {
+                strings.add(nextLine.trim());
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                zipFile.close();
+            } catch (IOException e) {
+                // Close quietly
+            }
+
+        }
+        return strings;
+
+    }
+
+    private SegmentedChromosomeData readChromosomeData(String chr) {
+        ZipFile zipFile = null;
+        BufferedInputStream is = null;
+        try {
+            String entryName = "data/" + chr + ".bin";
+            zipFile = new ZipFile(new File(filePath));
+            ZipEntry entry = zipFile.getEntry(entryName);
+            if (entry == null) {
+                return null;
+            }
+
+            is = new BufferedInputStream(zipFile.getInputStream(entry));
+
+            SegmentedChromosomeData cd = new SegmentedChromosomeData();
+            cd.deserialize(is);
+            return cd;
+
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        } finally {
+            try {
+                zipFile.close();
+            } catch (IOException e) {
+                // Close quietly
+            }
+        }
+
+    }
+
+    /**
+     * Read the contents of a zip entry as an array of ints.
+     *
+     * @param entryName name of the entry
+     * @return array of ints
+     */
+    private int[] readAsInts(String entryName) {
+
+        ZipFile zipFile = null;
+        try {
+            zipFile = new ZipFile(new File(filePath));
+            ZipEntry entry = zipFile.getEntry(entryName);
+            if (entry == null) {
+                return null;
+            }
+
+            DataInputStream is = new DataInputStream(
+                    new BufferedInputStream(zipFile.getInputStream(entry)));
+
+            int nInts = (int) (entry.getSize() / 4);
+            int[] ints = new int[nInts];
+            for (int i = 0; i <
+                    nInts; i++) {
+                ints[i] = is.readInt();
+            }
+
+            return ints;
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        } finally {
+            try {
+                zipFile.close();
+            } catch (IOException e) {
+                // close quietly
+            }
+        }
+
+    }
+
+    /**
+     * Read the content of a zip entry as an array of floats.
+     *
+     * @param entryName name of the entry
+     * @return contents as an array of floats
+     */
+    private float[] readAsFloats(String entryName) {
+
+        ZipFile zipFile = null;
+        try {
+            zipFile = new ZipFile(new File(filePath));
+            ZipEntry entry = zipFile.getEntry(entryName);
+            if (entry == null) {
+                return null;
+            }
+
+            DataInputStream is = new DataInputStream(
+                    new BufferedInputStream(zipFile.getInputStream(entry)));
+
+            int nInts = (int) (entry.getSize() / 4);
+            float[] values = new float[nInts];
+            for (int i = 0; i <
+                    nInts; i++) {
+                values[i] = is.readFloat();
+            }
+
+            return values;
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        } finally {
+            try {
+                zipFile.close();
+            } catch (IOException e) {
+
+            }
+        }
+
+
+    }
+
+}
+
+
diff --git a/src/org/broad/igv/data/seg/SegmentedBinaryReader.java b/src/org/broad/igv/data/seg/SegmentedBinaryReader.java
new file mode 100644
index 0000000..94a24a9
--- /dev/null
+++ b/src/org/broad/igv/data/seg/SegmentedBinaryReader.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.broad.igv.data.seg;
+
+import java.util.List;
+
+/**
+ * @author jrobinso
+ */
+public interface SegmentedBinaryReader {
+
+    SegmentedChromosomeData getChromosomeData(String chr);
+
+
+    /**
+     * Return the set of sample names in this data set
+     *
+     * @return
+     */
+    List<String> getSampleNames();
+
+    public String getStringAttribute(String key);
+
+}
diff --git a/src/org/broad/igv/data/seg/SegmentedBinaryRemoteReader.java b/src/org/broad/igv/data/seg/SegmentedBinaryRemoteReader.java
new file mode 100644
index 0000000..f61db21
--- /dev/null
+++ b/src/org/broad/igv/data/seg/SegmentedBinaryRemoteReader.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.data.seg;
+
+import org.apache.log4j.Logger;
+import org.broad.igv.util.IGVHttpUtils;
+import org.broad.igv.util.ResourceLocator;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author jrobinso
+ */
+public class SegmentedBinaryRemoteReader implements SegmentedBinaryReader {
+
+    static Logger log = Logger.getLogger(SegmentedBinaryRemoteReader.class);
+
+    String serverURL;
+
+    String filePath;
+
+    Map<String, String> attributes;
+
+    public SegmentedBinaryRemoteReader(ResourceLocator locator) {
+        this.serverURL = locator.getServerURL();
+        this.filePath = locator.getPath();
+    }
+
+    public SegmentedChromosomeData getChromosomeData(String chr) {
+
+        InputStream is = null;
+        try {
+            URL url = new URL(serverURL + "?method=getChromosomeData&file=" + filePath + "&chr=" + chr);
+            is = IGVHttpUtils.openConnectionStream(url);
+            SegmentedChromosomeData cd = new SegmentedChromosomeData();
+            cd.deserialize(is);
+            return cd;
+        }
+        catch (IOException ex) {
+            log.error("Error opening file", ex);
+            throw new RuntimeException(ex);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+
+                } catch (IOException iOException) {
+                    log.error("Error closing URL stream", iOException);
+                }
+            }
+
+        }
+    }
+
+    public List<String> getSampleNames() {
+        InputStream urlStream = null;
+
+        try {
+            List<String> childNames = new ArrayList(100);
+            URL url = new URL(serverURL + "?method=getSampleNames&file=" + filePath);
+            urlStream = IGVHttpUtils.openConnectionStream(url);
+            BufferedReader reader = new BufferedReader(new InputStreamReader(urlStream));
+            String nextLine = "";
+            while ((nextLine = reader.readLine()) != null) {
+                childNames.add(nextLine.trim());
+            }
+            reader.close();
+            return childNames;
+
+        } catch (IOException ex) {
+            //log.error("Error in getChildNames", ex);   
+            throw new RuntimeException(ex);
+        } finally {
+            if (urlStream != null) {
+                try {
+                    urlStream.close();
+
+                } catch (IOException iOException) {
+                    log.error("Error closing URL stream", iOException);
+                }
+            }
+
+        }
+    }
+    /*
+     *             
+    List<String> lines = readAsStrings("data/attributes.txt");
+    if (lines != null) {
+    for (String kv : lines) {
+    String[] tokens = kv.split("\t");
+    attrs.put(tokens[0], tokens[1]);
+    }
+    }
+     */
+
+    public String getStringAttribute(String key) {
+        if (attributes == null) {
+            attributes = new HashMap();
+            InputStream urlStream = null;
+
+            try {
+                URL url = new URL(serverURL + "?method=getAttributes&file=" + filePath);
+                urlStream = IGVHttpUtils.openConnectionStream(url);
+                BufferedReader reader = new BufferedReader(new InputStreamReader(urlStream));
+                String nextLine = "";
+                while ((nextLine = reader.readLine()) != null) {
+                    String[] tokens = nextLine.split("=");
+                    if (tokens.length > 1) {
+                        attributes.put(tokens[0], tokens[1]);
+                    }
+                }
+
+                reader.close();
+
+            } catch (IOException ex) {
+                log.error("Error in getChildNames", ex);
+
+            } finally {
+                if (urlStream != null) {
+                    try {
+                        urlStream.close();
+
+                    } catch (IOException iOException) {
+                        log.error("Error closing URL stream", iOException);
+                    }
+                }
+
+            }
+        }
+        return attributes.get(key);
+
+    }
+}
diff --git a/src/org/broad/igv/data/seg/SegmentedChromosomeData.java b/src/org/broad/igv/data/seg/SegmentedChromosomeData.java
new file mode 100644
index 0000000..980f0ab
--- /dev/null
+++ b/src/org/broad/igv/data/seg/SegmentedChromosomeData.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.data.seg;
+
+import org.apache.log4j.Logger;
+import org.broad.igv.data.CharArrayList;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * This class encapuslates data for a single chromosome from a segmented
+ * dataset.   Its main purpose is to provide a single object that can
+ * easily be serialized and passed from server to IGV clients.
+ *
+ * @author jrobinso
+ */
+public class SegmentedChromosomeData {
+
+    private static Logger log = Logger.getLogger(SegmentedChromosomeData.class);
+
+    private String[] sampleNames;
+
+    Map<String, int[]> startLocations;
+
+    Map<String, int[]> endLocations;
+
+    Map<String, float[]> valueMap;
+
+    public SegmentedChromosomeData() {
+    }
+
+    public SegmentedChromosomeData(
+            String[] sampleNames,
+            Map<String, int[]> startLocations,
+            Map<String, int[]> endLocations,
+            Map<String, float[]> valueMap) {
+        this.sampleNames = sampleNames;
+        this.startLocations = startLocations;
+        this.endLocations = endLocations;
+        this.valueMap = valueMap;
+    }
+
+    /**
+     * Serialize this instance to a stream.
+     * <p/>
+     * Format:
+     * Sample names as a string :
+     * sample1 tab sample2 tabnetc...  line-feed
+     * Followed by (for each sample)
+     * numberOfPoints (int)
+     * start locations (int array)
+     * end locations (int array)
+     * values (float array)
+     *
+     * @param stream stream to serialize to
+     */
+    public void serialize(OutputStream stream) {
+
+        try {
+            DataOutputStream os = new DataOutputStream(new BufferedOutputStream(stream));
+
+            for (String sample : getSampleNames()) {
+                os.writeChars(sample);
+                os.writeChar('\t');
+            }
+            os.writeChar('\n');
+            for (String sample : getSampleNames()) {
+                int[] starts = startLocations.get(sample);
+                int[] ends = endLocations.get(sample);
+                float[] values = valueMap.get(sample);
+                int nPts = starts.length;
+
+                assert ends.length == nPts;
+                assert values.length == nPts;
+
+                os.writeInt(nPts);
+                for (int i = 0; i < nPts; i++) {
+                    os.writeInt(starts[i]);
+                }
+                for (int i = 0; i < nPts; i++) {
+                    os.writeInt(ends[i]);
+                }
+                for (int i = 0; i < nPts; i++) {
+                    os.writeFloat(values[i]);
+                }
+            }
+            os.flush();
+
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Initialize the object by deserializing the stream.
+     *
+     * @param stream
+     * @see #serialize
+     */
+    public void deserialize(InputStream stream) {
+        startLocations = new HashMap();
+        endLocations = new HashMap();
+        valueMap = new HashMap();
+        try {
+
+            DataInputStream is = new DataInputStream(new BufferedInputStream(stream));
+
+            ArrayList<String> sampleNames = new ArrayList(100);
+            char c = is.readChar();
+            while (c != '\n') {
+                CharArrayList chars = new CharArrayList(10);
+                while (c != '\t') {
+                    chars.add(c);
+                    c = is.readChar();
+                }
+                String nm = new String(chars.toArray()).trim();
+                sampleNames.add(nm);
+                c = is.readChar();
+            }
+            
+
+            for (String sample : sampleNames) {
+                int nPts = is.readInt();
+                int[] starts = new int[nPts];
+                for (int i = 0; i < nPts; i++) {
+                    starts[i] = is.readInt();
+                }
+                int[] ends = new int[nPts];
+                for (int i = 0; i < nPts; i++) {
+                    ends[i] = is.readInt();
+                }
+                float[] values = new float[nPts];
+                for (int i = 0; i < nPts; i++) {
+                    values[i] = is.readFloat();
+                }
+                startLocations.put(sample, starts);
+                endLocations.put(sample, ends);
+                valueMap.put(sample, values);
+            }
+
+        }
+        catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public String[] getSampleNames() {
+        return sampleNames;
+    }
+
+    public int[] getStartLocations(String sampleName) {
+        return startLocations.get(sampleName);
+    }
+
+    public int[] getEndLocations(String sampleName) {
+        return endLocations.get(sampleName);
+    }
+
+    public float[] getValues(String sampleName) {
+        return valueMap.get(sampleName);
+    }
+}
diff --git a/src/org/broad/igv/data/seg/SegmentedDataSet.java b/src/org/broad/igv/data/seg/SegmentedDataSet.java
new file mode 100644
index 0000000..612a7ec
--- /dev/null
+++ b/src/org/broad/igv/data/seg/SegmentedDataSet.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.broad.igv.data.seg;
+
+import org.broad.igv.feature.LocusScore;
+import org.broad.igv.track.TrackType;
+
+import java.util.List;
+
+/**
+ * @author jrobinso
+ */
+public interface SegmentedDataSet {
+
+    double getDataMax(String chr);
+
+    double getDataMin(String chr);
+
+    List<String> getSampleNames();
+
+    List<LocusScore> getSegments(String heading, String chr);
+
+    TrackType getType();
+
+    List<LocusScore> getWholeGenomeScores(String heading);
+
+    boolean isLogNormalized();
+
+    void refreshData(long timestamp);
+
+}
diff --git a/src/org/broad/igv/data/seg/SegmentedDataSource.java b/src/org/broad/igv/data/seg/SegmentedDataSource.java
new file mode 100644
index 0000000..a545feb
--- /dev/null
+++ b/src/org/broad/igv/data/seg/SegmentedDataSource.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.data.seg;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import org.broad.igv.Globals;
+import org.broad.igv.data.DataSource;
+import org.broad.igv.feature.LocusScore;
+import org.broad.igv.track.TrackType;
+import org.broad.igv.track.WindowFunction;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * TODO -- THIS IS NEARLY AN EXACT COPY OF SegmentedDataSource,  only the
+ * dataset type is different.
+ *
+ * @author jrobinso
+ */
+public class SegmentedDataSource implements DataSource {
+
+    /**
+     * Identifies the track.  Often a sample name.
+     */
+    String trackIdentifier;
+    SegmentedDataSet dataset;
+
+    /**
+     * Constructs ...
+     *
+     * @param trackIdentifier
+     * @param dataset
+     */
+    public SegmentedDataSource(String trackIdentifier, SegmentedDataSet dataset) {
+        this.trackIdentifier = trackIdentifier;
+        this.dataset = dataset;
+    }
+
+
+    public void refreshData(long timestamp) {
+        dataset.refreshData(timestamp);
+    }
+
+
+    public TrackType getTrackType() {
+        return dataset.getType();
+    }
+
+
+    public void setWindowFunction(WindowFunction statType) {
+
+        // ignore
+    }
+
+
+    public boolean isLogNormalized() {
+        return dataset.isLogNormalized();
+    }
+
+
+    public double getDataMax() {
+        return dataset.getDataMax(Globals.CHR_ALL);
+    }
+
+
+    public double getDataMin() {
+        return dataset.getDataMin(Globals.CHR_ALL);
+    }
+
+
+    public double getMedian(int zoom, String chr) {
+        return 1.0;
+    }
+
+    private List<LocusScore> getSegments(String chr) {
+
+        return dataset.getSegments(trackIdentifier, chr);
+
+    }
+
+
+    public List<LocusScore> getSummaryScoresForRange(String chr, int startLocation,
+                                                     int endLocation, int zoom) {
+        if (chr.equals(Globals.CHR_ALL)) {
+            return getWholeGenomeScores();
+        }
+        return getSegments(chr);
+    }
+
+
+    public List<LocusScore> getWholeGenomeScores() {
+        return dataset.getWholeGenomeScores(trackIdentifier);
+    }
+
+
+    public Collection<WindowFunction> getAvailableWindowFunctions() {
+        return new ArrayList();
+    }
+
+    
+    public WindowFunction getWindowFunction() {
+        return null;
+    }
+}
diff --git a/src/org/broad/igv/data/seg/SegmentedDataWriter.java b/src/org/broad/igv/data/seg/SegmentedDataWriter.java
new file mode 100644
index 0000000..65f63c5
--- /dev/null
+++ b/src/org/broad/igv/data/seg/SegmentedDataWriter.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.data.seg;
+
+import org.apache.log4j.Logger;
+import org.broad.igv.Globals;
+import org.broad.igv.feature.LocusScore;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.track.TrackType;
+import org.broad.igv.util.ResourceLocator;
+import org.broad.igv.util.Utilities;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * @author jrobinso
+ */
+public class SegmentedDataWriter {
+
+    private static Logger log = Logger.getLogger(SegmentedDataWriter.class);
+
+    private boolean compress = true;
+
+    SegmentedAsciiDataSet dataset;
+
+    String rootNodeName;
+
+    File outputFile;
+
+    TrackType trackType;
+
+    public SegmentedDataWriter(SegmentedAsciiDataSet dataset, File outputFile) {
+        this(dataset, outputFile, false, TrackType.COPY_NUMBER);
+    }
+
+    public SegmentedDataWriter(SegmentedAsciiDataSet dataset, File outputFile, boolean compress, TrackType type) {
+        this.dataset = dataset;
+        this.compress = compress;
+        this.outputFile = outputFile;
+        this.rootNodeName = "data";
+        this.trackType = type;
+    }
+
+    public void writeContents() {
+
+        ZipOutputStream zos = null;
+
+        try {
+            zos = new ZipOutputStream(new FileOutputStream(outputFile));
+            List<String> samples = dataset.getDataHeadings();
+            String[] sampleArray = samples.toArray(new String[]{});
+            Set<String> chromosomes = dataset.getChromosomes();
+
+            List<String> attributes = new ArrayList();
+            attributes.add("type=" + trackType.toString());
+            attributes.add("logNormalized=" + dataset.isLogNormalized());
+            writeStrings("attributes.txt", attributes, zos);
+
+            writeStrings("samples.txt", samples, zos);
+            writeStrings("chromosomes.txt", chromosomes, zos);
+
+
+            ArrayList<String> tmp = new ArrayList(dataset.getChromosomes());
+            tmp.add(Globals.CHR_ALL);
+            for (String chr : tmp) {
+                Map<String, int[]> starts = new HashMap();
+                Map<String, int[]> ends = new HashMap();
+                Map<String, float[]> values = new HashMap();
+                for (String sample : samples) {
+
+                    List<LocusScore> segments = chr.equals(Globals.CHR_ALL) ? dataset.getWholeGenomeScores(sample) : dataset.getSegments(sample, chr);
+                    int nPts = segments == null ? 0 : segments.size();
+                    int[] s = new int[nPts];
+                    int[] e = new int[nPts];
+                    float[] v = new float[nPts];
+                    for (int i = 0; i < nPts; i++) {
+                        s[i] = segments.get(i).getStart();
+                        e[i] = segments.get(i).getEnd();
+                        v[i] = segments.get(i).getScore();
+                    }
+
+                    starts.put(sample, s);
+                    ends.put(sample, e);
+                    values.put(sample, v);
+
+                    //writeSegments(sample, chr, segments, zos);
+                }
+                SegmentedChromosomeData cd = new SegmentedChromosomeData(
+                        sampleArray, starts, ends, values);
+                writeChromsomeData(chr, cd, zos);
+
+            }
+
+        } catch (IOException e) {
+            e.printStackTrace();
+
+        } finally {
+            try {
+                zos.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private void writeStrings(String filename, Collection<String> strings, ZipOutputStream zos) {
+        try {
+            // Put strings in a byte buffer.  We need to know the total size in bytes
+            StringBuffer buff = new StringBuffer();
+            for (String s : strings) {
+                buff.append(s);
+                buff.append('\n');
+            }
+            byte[] bytes = buff.toString().getBytes();
+
+            String entryName = rootNodeName + "/" + filename;
+            Utilities.createZipEntry(entryName, bytes, zos, compress);
+
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        } finally {
+        }
+    }
+
+    /**
+     * Write the segment data from a single chromosome for a sample.  The data
+     * is organized in the following hierarchical structure:
+     * <p/>
+     * data/<sample>/<chr>/starts.bin
+     * data/<sample>/<chr>/ends.bin
+     * data/<sample>/<chr>/values.bin
+     *
+     * @param sampleName
+     * @param chr
+     * @param segments
+     */
+    private void writeChromsomeData(String chr, SegmentedChromosomeData cd, ZipOutputStream zos) {
+        try {
+
+            ByteArrayOutputStream bytes = new ByteArrayOutputStream(1000);
+            BufferedOutputStream bos = new BufferedOutputStream(bytes);
+            cd.serialize(bytes);
+            bos.close();
+
+            byte[] byteArray = bytes.toByteArray();
+
+            String entryName = rootNodeName + "/" + chr + ".bin";
+            Utilities.createZipEntry(entryName, byteArray, zos, compress);
+
+        } catch (IOException ex) {
+            log.error("Error writing segments", ex);
+        } finally {
+        }
+    }
+
+    public static void main(String[] args) {
+
+
+        String inputFile = null;
+        String outputFile = null;
+        String genomeId = null;
+        boolean compress = true;
+        TrackType trackType = TrackType.COPY_NUMBER;
+        if (args.length > 2) {
+            inputFile = args[0].trim();
+            outputFile = args[1].trim();
+            genomeId = args[2].trim();
+        } else {
+            System.out.println("Arguments: inputFile  outputFile  genomeId  [track type (optional)");
+            System.exit(-1);
+        }
+        if (args.length > 3) {
+            try {
+                trackType = TrackType.valueOf(args[3].trim());
+            } catch (Exception e) {
+                System.out.println("Unknown track type: " + args[3]);
+            }
+        }
+
+        //inputFile = "/Users/jrobinso/IGVTestData/Demo/TCGA/broad.seg";
+        //outputFile = "test.seg.zip";
+
+        // TODO -- remove the need for this hack
+        ViewContext.getInstance().setGenomeId(genomeId);
+
+        SegmentedAsciiDataSet ds = new SegmentedAsciiDataSet(new ResourceLocator(inputFile));
+
+        SegmentedDataWriter writer = new SegmentedDataWriter(ds, new File(outputFile), compress, trackType);
+
+        writer.writeContents();
+
+    }
+
+    public void setCompress(
+
+            boolean compress) {
+        this.compress = compress;
+    }
+}
diff --git a/src/org/broad/igv/event/StatusChangeEvent.java b/src/org/broad/igv/event/StatusChangeEvent.java
index 1fe07aa..107c664 100644
--- a/src/org/broad/igv/event/StatusChangeEvent.java
+++ b/src/org/broad/igv/event/StatusChangeEvent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/exceptions/DataLoadException.java b/src/org/broad/igv/exceptions/DataLoadException.java
index 5a7d9ec..9bfe8cb 100644
--- a/src/org/broad/igv/exceptions/DataLoadException.java
+++ b/src/org/broad/igv/exceptions/DataLoadException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -19,23 +19,20 @@
 package org.broad.igv.exceptions;
 
 /**
- * Created by IntelliJ IDEA.
- * User: nazaire
+ * Author: nazaire
  * Date: Jul 8, 2009
  */
 public class DataLoadException extends RuntimeException {
+
+    String message;
     private String fileName;
 
     public DataLoadException(String message, String fileName) {
-        super(message);
+        if(message != null) this.message = message.replace("<html>", "");
         this.fileName = fileName;
     }
 
-    public String getFileName() {
-        return fileName;
-    }
-
     public String getMessage() {
-        return "An error occurred while loading  " + fileName + ":\n" + super.getMessage();
+        return "<html>An error occurred while loading:    " + fileName + "<br>" + message;
     }
 }
\ No newline at end of file
diff --git a/src/org/broad/igv/exceptions/LoadResourceFromServerException.java b/src/org/broad/igv/exceptions/LoadResourceFromServerException.java
index 008363d..c8527ce 100644
--- a/src/org/broad/igv/exceptions/LoadResourceFromServerException.java
+++ b/src/org/broad/igv/exceptions/LoadResourceFromServerException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/exceptions/ParserException.java b/src/org/broad/igv/exceptions/ParserException.java
index 6d7777f..fadcfa9 100644
--- a/src/org/broad/igv/exceptions/ParserException.java
+++ b/src/org/broad/igv/exceptions/ParserException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -25,7 +25,6 @@ package org.broad.igv.exceptions;
  */
 public class ParserException extends RuntimeException {
     private long lineNumber = -1;
-    private String fileName;
     private String line;
 
     public ParserException(String message, long lineNumber) {
@@ -56,27 +55,11 @@ public class ParserException extends RuntimeException {
         setLine(line);
     }
 
-    public void setFileName(String fileName) {
-        if (fileName == null) {
-            throw new NullPointerException("File name in ParserException cannot be null.");
-        }
-        this.fileName = fileName;
-    }
-
-    public String getFileName() {
-        return fileName;
-    }
 
     public void setLineNumber(long lineNumber) {
-        if (lineNumber < 1) {
-            throw new IllegalArgumentException("ParsingException line number must be an integer greater than or equal to 1");
-        }
         this.lineNumber = lineNumber;
     }
 
-    public long getLineNumber() {
-        return lineNumber;
-    }
 
     public void setLine(String line) {
         if (line != null) {
@@ -87,10 +70,6 @@ public class ParserException extends RuntimeException {
         }
     }
 
-    public String getLine() {
-        return line;
-    }
-
     public String getMessage() {
         String message = super.getMessage();
         if (message == null)
diff --git a/src/org/broad/igv/exceptions/ProbeMappingException.java b/src/org/broad/igv/exceptions/ProbeMappingException.java
index a2933a9..f46a1aa 100644
--- a/src/org/broad/igv/exceptions/ProbeMappingException.java
+++ b/src/org/broad/igv/exceptions/ProbeMappingException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -40,6 +40,6 @@ public class ProbeMappingException extends RuntimeException {
     }
 
     public String getMessage() {
-        return "The probes in the dataset could not be mapped to locations on the loaded genome (" + genome + ").";
+        return "The probes in the dataset " + fileName + "  could not be mapped to locations on the loaded genome (" + genome + ").";
     }
 }
diff --git a/src/org/broad/igv/feature/AbstractFeature.java b/src/org/broad/igv/feature/AbstractFeature.java
index 86d6749..7bcd1c4 100644
--- a/src/org/broad/igv/feature/AbstractFeature.java
+++ b/src/org/broad/igv/feature/AbstractFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -20,7 +20,6 @@ package org.broad.igv.feature;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
-import org.broad.igv.util.ParsingUtils;
 
 import java.awt.*;
 import java.util.List;
@@ -29,7 +28,7 @@ import java.util.Map;
 /**
  * @author jrobinso
  */
-abstract public class AbstractFeature implements Feature {
+abstract public class AbstractFeature implements Feature, org.broad.tribble.Feature {
 
     private static Logger log = Logger.getLogger(AbstractFeature.class);
     private Strand strand = Strand.NONE;
@@ -152,7 +151,7 @@ abstract public class AbstractFeature implements Feature {
      *
      * @return
      */
-    public String getChromosome() {
+    public String getChr() {
         return chromosome;
     }
 
@@ -196,7 +195,7 @@ abstract public class AbstractFeature implements Feature {
         if (feature == null) {
             return false;
         }
-        if (!this.getChromosome().equals(feature.getChromosome()) ||
+        if (!this.getChr().equals(feature.getChr()) ||
                 this.getStrand() != feature.getStrand()) {
             return false;
         }
@@ -277,6 +276,9 @@ abstract public class AbstractFeature implements Feature {
      */
     public void setColor(String[] rgb, int nTokens) {
         try {
+            if(rgb[0].equals(".")) {
+                return;
+            }
             if (nTokens < 3) {
                 color = new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[0]),
                         Integer.parseInt(rgb[0]));
@@ -285,7 +287,7 @@ abstract public class AbstractFeature implements Feature {
                         Integer.parseInt(rgb[2]));
             }
         } catch (NumberFormatException numberFormatException) {
-            log.error("Error parsing color from rgb string: " + rgb[0] + rgb[1] + rgb[2]);
+            // Just skip
         }
     }
 
@@ -319,7 +321,7 @@ abstract public class AbstractFeature implements Feature {
     /**
      * @param chromosome the chromosome to set
      */
-    public void setChromosome(String chromosome) {
+    public void setChr(String chromosome) {
         this.chromosome = chromosome;
     }
 }
diff --git a/src/org/broad/igv/feature/AbstractFeatureParser.java b/src/org/broad/igv/feature/AbstractFeatureParser.java
index d5d2aac..5cae324 100644
--- a/src/org/broad/igv/feature/AbstractFeatureParser.java
+++ b/src/org/broad/igv/feature/AbstractFeatureParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,15 +23,17 @@ package org.broad.igv.feature;
 
 import org.apache.log4j.Logger;
 import org.broad.igv.exceptions.ParserException;
+import org.broad.igv.exceptions.DataLoadException;
 import org.broad.igv.renderer.BasicFeatureRenderer;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.track.FeatureCollectionSource;
 import org.broad.igv.track.FeatureTrack;
 import org.broad.igv.track.TrackProperties;
 import org.broad.igv.track.TrackType;
 import org.broad.igv.ui.IGVMainFrame;
-import org.broad.igv.ui.IGVModel;
 import org.broad.igv.util.AsciiLineReader;
-import org.broad.igv.util.ResourceLocator;
 import org.broad.igv.util.ParsingUtils;
+import org.broad.igv.util.ResourceLocator;
 
 import java.io.FileWriter;
 import java.io.IOException;
@@ -40,7 +42,6 @@ import java.util.ArrayList;
 import java.util.List;
 
 /**
- *
  * @author jrobinso
  */
 public abstract class AbstractFeatureParser implements FeatureParser {
@@ -48,13 +49,15 @@ public abstract class AbstractFeatureParser implements FeatureParser {
     private static Logger log = Logger.getLogger(IGVMainFrame.class);
     private String filePath;
     protected Genome genome;
+    protected int startBase = 0;
 
     /* An object to collection track properties, if specified in the feature file. */
     private TrackProperties trackProperties = null;
 
+    TrackType trackType;
 
-    public  AbstractFeatureParser() {
-        genome = IGVModel.getInstance().getViewContext().getGenome();
+    public AbstractFeatureParser() {
+        genome = ViewContext.getInstance().getGenome();
     }
 
     /**
@@ -67,6 +70,8 @@ public abstract class AbstractFeatureParser implements FeatureParser {
 
         if (tmp.endsWith("bed") || tmp.endsWith("map")) {
             return new BEDFileParser();
+        } else if (tmp.endsWith("psl")) {
+            return new PSLParser();
         } else if (tmp.contains("refflat")) {
             return new UCSCGeneTableParser(UCSCGeneTableParser.Type.REFFLAT);
         } else if (tmp.contains("genepred") || tmp.contains("ensgene") || tmp.contains("refgene")) {
@@ -78,36 +83,16 @@ public abstract class AbstractFeatureParser implements FeatureParser {
         } else if (tmp.endsWith("embl")) {
             return new EmblFeatureTableParser();
         } else {
-            return new BEDFileParser();
+            return null;
         }
     }
 
-    public static boolean isVersion3(ResourceLocator locator) {
-        AsciiLineReader reader = null;
-
-        try {
-            reader = ParsingUtils.openAsciiReader(locator);
-            String firstLine = reader.readLine().trim();
-            return (firstLine.startsWith("##gff-version") && firstLine.endsWith("3"));
-        }
-        catch (IOException e) {
-            //log.error("Error getting GFF version", e);
-            return false;
-        }
-        finally {
-            if (reader != null) {
-                reader.close();
-            }
-        }
-
-
-    }
 
     /**
      * Return true if the file represented by the locator contains feature.  This method
      * returns true by default.  It can be overriden by subclasses representing ambiguous
      * file content.
-     * 
+     *
      * @param locator
      * @return true if a feature file
      */
@@ -120,20 +105,24 @@ public abstract class AbstractFeatureParser implements FeatureParser {
      * Load all features in this file.
      *
      * @param locator
-     *
      * @return
      */
     public List<FeatureTrack> loadTracks(ResourceLocator locator) {
 
-        FeatureTrack track = new FeatureTrack(locator, locator.getDisplayName());
-
-        List<Feature> features = loadFeatures(track, locator, -1);
-        track.initFeatures(features);
-
+        List<Feature> features = loadFeatures(locator, -1);
+        if (features.size() == 0) {
+            throw new DataLoadException("No features were found in this file with chromosomes mapped to the current genome", locator.getPath());
+        }
+        FeatureCollectionSource source = new FeatureCollectionSource(features);
+        FeatureTrack track = new FeatureTrack(locator, source);
+        track.setName(locator.getTrackName());
         track.setRendererClass(BasicFeatureRenderer.class);
         track.setMinimumHeight(35);
         track.setHeight(45);
         //track.setRendererClass(GeneTrackRenderer.class);
+        if (trackType != null) {
+            track.setTrackType(trackType);
+        }
         if (trackProperties != null) {
             track.setTrackProperties(trackProperties);
         }
@@ -146,21 +135,18 @@ public abstract class AbstractFeatureParser implements FeatureParser {
     /**
      * Parse a limited number of lines in this file and return a list of features found.
      *
-     *
      * @param locator
      * @param maxLines
-     *
      * @return
      */
-    protected List<Feature> loadFeatures(FeatureTrack track, ResourceLocator locator, int maxLines) {
+    protected List<Feature> loadFeatures(ResourceLocator locator, int maxLines) {
 
         // File file = new File(locator.getPath());
         int nLines = 0;
         AsciiLineReader reader = null;
-        List<Feature> features = new ArrayList<Feature>();
         try {
             reader = ParsingUtils.openAsciiReader(locator);
-            return loadFeatures(track, reader, maxLines);
+            return loadFeatures(reader, maxLines);
         } catch (IOException e) {
             throw new RuntimeException(e);
         } finally {
@@ -170,33 +156,24 @@ public abstract class AbstractFeatureParser implements FeatureParser {
         }
     }
 
-    protected List<Feature> loadFeatures(ResourceLocator locator, int maxLines) {
-        return loadFeatures(null, locator, maxLines);
-    }
-
     /**
      * Method description
      *
-     *
      * @param reader
-     *
      * @return
      */
     public List<Feature> loadFeatures(AsciiLineReader reader) {
-        return loadFeatures(null, reader, -1);
+        return loadFeatures(reader, -1);
     }
 
     /**
      * Load all features in this file.
      *
-     *
      * @param reader
      * @param maxLines
-     *
      * @return
-     *
      */
-    public List<Feature> loadFeatures(FeatureTrack track, AsciiLineReader reader, int maxLines) {
+    public List<Feature> loadFeatures(AsciiLineReader reader, int maxLines) {
 
         List<Feature> features = new ArrayList<Feature>();
         String nextLine = null;
@@ -212,12 +189,11 @@ public abstract class AbstractFeatureParser implements FeatureParser {
 
                 try {
                     if (nextLine.startsWith("#")) {
-                        if (track != null && nextLine.startsWith("#type")) {
+                        if (nextLine.startsWith("#type")) {
                             String[] tokens = nextLine.split("=");
                             if (tokens.length > 1) {
                                 try {
                                     TrackType type = TrackType.valueOf(tokens[1]);
-                                    track.setTrackType(type);
                                 } catch (Exception e) {
                                     log.error("Error converting track type: " + tokens[1]);
                                 }
@@ -226,6 +202,15 @@ public abstract class AbstractFeatureParser implements FeatureParser {
                             TrackProperties tp = new TrackProperties();
                             ParsingUtils.parseTrackLine(nextLine, tp);
                             setTrackProperties(tp);
+                        } else if (nextLine.startsWith("#coords")) {
+                            try {
+                                String[] tokens = nextLine.split("=");
+                                startBase = Integer.parseInt(tokens[1]);
+                            }
+                            catch (Exception e) {
+                                log.error("Error parsing coords line: " + nextLine, e);
+                            }
+
                         }
                     } else {
                         Feature feature = parseLine(nextLine);
@@ -295,7 +280,7 @@ public abstract class AbstractFeatureParser implements FeatureParser {
             for (Feature gene : features) {
                 pw.print(gene.getName() + "\t");
                 pw.print(gene.getIdentifier() + "\t");
-                pw.print(gene.getChromosome() + "\t");
+                pw.print(gene.getChr() + "\t");
                 if (gene.getStrand() == Strand.POSITIVE) {
                     pw.print("+\t");
                 } else if (gene.getStrand() == Strand.NEGATIVE) {
@@ -330,4 +315,8 @@ public abstract class AbstractFeatureParser implements FeatureParser {
     protected void setTrackProperties(TrackProperties trackProperties) {
         this.trackProperties = trackProperties;
     }
+
+    public TrackProperties getTrackProperties() {
+        return trackProperties;
+    }
 }
diff --git a/src/org/broad/igv/feature/AminoAcid.java b/src/org/broad/igv/feature/AminoAcid.java
index 00c4b5c..265c096 100644
--- a/src/org/broad/igv/feature/AminoAcid.java
+++ b/src/org/broad/igv/feature/AminoAcid.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/feature/AminoAcidManager.java b/src/org/broad/igv/feature/AminoAcidManager.java
index 8d49c16..cd67ac8 100644
--- a/src/org/broad/igv/feature/AminoAcidManager.java
+++ b/src/org/broad/igv/feature/AminoAcidManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -53,7 +53,6 @@ public class AminoAcidManager {
      *
      * @param startPosition
      * @param endPosition
-     * @param direction
      * @return
      */
     public static AminoAcidSequence getAminoAcidSequence(String genomeId, String chr,
@@ -96,11 +95,15 @@ public class AminoAcidManager {
             if (direction == Strand.NEGATIVE) {
                 codon = getNucleotideComplement(codon);
             }
-            AminoAcid aa = codonTable.get(codon);
-            if (aa == null) {
-                aa = AminoAcid.NULL_AMINO_ACID;
+            if (codonTable == null || codon == null) {
+                acids.add(AminoAcid.NULL_AMINO_ACID);
+            } else {
+                AminoAcid aa = codonTable.get(codon);
+                if (aa == null) {
+                    aa = AminoAcid.NULL_AMINO_ACID;
+                }
+                acids.add(codonTable.get(codon));
             }
-            acids.add(codonTable.get(codon));
 
         }
         return acids;
@@ -134,12 +137,15 @@ public class AminoAcidManager {
     static synchronized void initTable() {
 
         if (codonTable == null) {
-            codonTable = new HashMap();
 
 
             try {
 
                 InputStream is = AminoAcidManager.class.getResourceAsStream("/resources/" + filename);
+                if (is == null) {
+                    return;
+                }
+                codonTable = new HashMap();
                 BufferedReader reader = new BufferedReader(new InputStreamReader(is));
 
                 String nextLine = null;
diff --git a/src/org/broad/igv/feature/AminoAcidSequence.java b/src/org/broad/igv/feature/AminoAcidSequence.java
index cfdd1c8..70083ae 100644
--- a/src/org/broad/igv/feature/AminoAcidSequence.java
+++ b/src/org/broad/igv/feature/AminoAcidSequence.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/feature/BEDFileParser.java b/src/org/broad/igv/feature/BEDFileParser.java
index d0c6202..8ebfd65 100644
--- a/src/org/broad/igv/feature/BEDFileParser.java
+++ b/src/org/broad/igv/feature/BEDFileParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -20,9 +20,8 @@ package org.broad.igv.feature;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
-import org.broad.igv.ui.IGVModel;
-import org.broad.igv.util.ResourceLocator;
 import org.broad.igv.util.ParsingUtils;
+import org.broad.igv.util.ResourceLocator;
 
 /**
  * Class description
@@ -47,11 +46,11 @@ public class BEDFileParser extends UCSCParser {
      */
     public boolean isFeatureFile(ResourceLocator locator) {
         String ext = getStrippedFilename(locator.getPath());
-        return ext.endsWith("bed") || loadFeatures(locator, 20).size() > 0;
+        return ext.endsWith("bed");
 
     }
 
-    protected BasicFeature parseLine(String[] tokens, int tokenCount) {
+    public BasicFeature parseLine(String[] tokens, int tokenCount) {
 
 
         // The first 3 columns are non optional for BED.  We will relax this
@@ -61,8 +60,8 @@ public class BEDFileParser extends UCSCParser {
             return null;
         }
 
-        String chr = genome.getChromosomeAlias(tokens[0]);
-        int start = Integer.parseInt(tokens[1]);
+        String chr = genome == null ? tokens[0] : genome.getChromosomeAlias(tokens[0]);
+        int start = Integer.parseInt(tokens[1]) - startBase;
 
         int end = start + 1;
         if (tokenCount > 2) {
@@ -110,6 +109,12 @@ public class BEDFileParser extends UCSCParser {
             }
         }
 
+        // thick start & end
+        if (tokenCount > 7) {
+            feature.setThickStart(Integer.parseInt(tokens[6]));
+            feature.setThickEnd(Integer.parseInt(tokens[7]));
+        }
+
         if (tokenCount > 8) {
             String[] rgb = new String[3];
             int nTokens = ParsingUtils.split(tokens[8].replaceAll("\"", ""), rgb, ',');
@@ -127,7 +132,7 @@ public class BEDFileParser extends UCSCParser {
     private void createExons(int start, String[] tokens, BasicFeature gene, String chr,
                              Strand strand) throws NumberFormatException {
 
-        int cdStart = Integer.parseInt(tokens[6]);
+        int cdStart = Integer.parseInt(tokens[6]) - startBase;
         int cdEnd = Integer.parseInt(tokens[7]);
 
         int exonCount = Integer.parseInt(tokens[9]);
diff --git a/src/org/broad/igv/feature/BasicFeature.java b/src/org/broad/igv/feature/BasicFeature.java
index 47cc752..bf62512 100644
--- a/src/org/broad/igv/feature/BasicFeature.java
+++ b/src/org/broad/igv/feature/BasicFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -29,7 +29,8 @@ import java.util.List;
 
 /**
  * A convenience class providing default implementation for many Feature
- *  methods.
+ * methods.
+ *
  * @author jrobinso
  */
 public class BasicFeature extends AbstractFeature {
@@ -38,15 +39,25 @@ public class BasicFeature extends AbstractFeature {
     protected List<Exon> exons;
     protected int level = 1;
     private String type;
-    protected String name = "";
     protected float score = Float.NaN;
     protected float confidence;
     String identifier;
+    private int thickEnd;
+    private int thickStart;
+
+    public String[] getParentIds() {
+        return parentIds;
+    }
+
+    public void setParentIds(String[] parentIds) {
+        this.parentIds = parentIds;
+    }
+
+    String [] parentIds;
     String link;
 
     /**
      * Constructs ...
-     *
      */
     public BasicFeature() {
     }
@@ -54,7 +65,6 @@ public class BasicFeature extends AbstractFeature {
     /**
      * Constructs ...
      *
-     *
      * @param chr
      * @param start
      * @param end
@@ -62,12 +72,13 @@ public class BasicFeature extends AbstractFeature {
     public BasicFeature(String chr, int start, int end) {
 
         this(chr, start, end, Strand.NONE);
+        this.thickStart = start;
+        this.thickEnd = end;
     }
 
     /**
      * Constructs ...
      *
-     *
      * @param chr
      * @param start
      * @param end
@@ -75,17 +86,19 @@ public class BasicFeature extends AbstractFeature {
      */
     public BasicFeature(String chr, int start, int end, Strand strand) {
         super(chr, start, end, strand);
+        this.thickStart = start;
+        this.thickEnd = end;
+
     }
 
     /**
      * Constructs ...
      *
-     *
      * @param feature
      */
     public BasicFeature(BasicFeature feature) {
-        super(feature.getChromosome(), feature.getStart(), feature.getEnd(), feature.getStrand());
-        this.name = feature.name;
+        super(feature.getChr(), feature.getStart(), feature.getEnd(), feature.getStrand());
+        super.setName(feature.getName());
         this.confidence = feature.confidence;
         this.color = feature.color;
         this.description = feature.description;
@@ -95,12 +108,13 @@ public class BasicFeature extends AbstractFeature {
         this.identifier = feature.identifier;
         this.type = feature.type;
         this.link = feature.link;
+        this.thickStart = feature.thickStart;
+        this.thickEnd = feature.thickEnd;
     }
 
     /**
      * Method description
      *
-     *
      * @return
      */
     public BasicFeature copy() {
@@ -108,54 +122,35 @@ public class BasicFeature extends AbstractFeature {
     }
 
     /**
-     *
-     *
      * @param identifier
      */
     public void setIdentifier(String identifier) {
         this.identifier = identifier;
     }
 
-    /**
-     *
-     *
-     * @param name
-     */
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    /**
-     *
-     *
-     * @return
-     */
-    public String getName() {
-        return name;
-    }
 
     /**
      * Return a string for popup text, and related uses.  The default just
      * returns the feature name.  Its expected that this method will be
      * overriden in subclasses.
      *
-     *
-     *
-     * @param ignored
-     * @return
+     * @position -- 1 based coordinates
      */
     public String getValueString(double position, WindowFunction ignored) {
         StringBuffer valueString = new StringBuffer();
 
         // Get exon number, if over an exon
+        int posZero = (int) position - 1;
         if (this.exons != null) {
             for (Exon exon : exons) {
-                if (position >= exon.getStart() && position <= exon.getEnd()) {
+                if (posZero >= exon.getStart() && posZero < exon.getEnd()) {
                     valueString.append(exon.getValueString(position, ignored) + "<br>");
+                    return valueString.toString();
                 }
             }
         }
 
+        String name = getName();
         if (name != null) {
             valueString.append(name);
         }
@@ -174,7 +169,6 @@ public class BasicFeature extends AbstractFeature {
     /**
      * Method description
      *
-     *
      * @param score
      */
     public void setScore(float score) {
@@ -184,7 +178,6 @@ public class BasicFeature extends AbstractFeature {
     /**
      * Method description
      *
-     *
      * @return
      */
     @Override
@@ -195,7 +188,6 @@ public class BasicFeature extends AbstractFeature {
     /**
      * Method description
      *
-     *
      * @return
      */
     @Override
@@ -206,27 +198,6 @@ public class BasicFeature extends AbstractFeature {
     /**
      * Method description
      *
-     *
-     * @param level
-     */
-    public void setLevel(int level) {
-        this.level = level;
-    }
-
-    /**
-     * Method description
-     *
-     *
-     * @return
-     */
-    public int getLevel() {
-        return level;
-    }
-
-    /**
-     * Method description
-     *
-     *
      * @return
      */
     @Override
@@ -236,7 +207,6 @@ public class BasicFeature extends AbstractFeature {
 
     /**
      * Method description
-     *
      */
     public void sortExons() {
         if (exons != null) {
@@ -252,7 +222,6 @@ public class BasicFeature extends AbstractFeature {
     /**
      * Method description
      *
-     *
      * @param region
      */
     public void addExon(Exon region) {
@@ -267,9 +236,7 @@ public class BasicFeature extends AbstractFeature {
     /**
      * Method description
      *
-     *
      * @param o
-     *
      * @return
      */
     public int compareTo(Object o) {
@@ -280,7 +247,6 @@ public class BasicFeature extends AbstractFeature {
     /**
      * Method description
      *
-     *
      * @return
      */
     @Override
@@ -291,7 +257,6 @@ public class BasicFeature extends AbstractFeature {
     /**
      * Method description
      *
-     *
      * @return
      */
     public int getExonCount() {
@@ -301,7 +266,6 @@ public class BasicFeature extends AbstractFeature {
     /**
      * Method description
      *
-     *
      * @return
      */
     public String getType() {
@@ -311,18 +275,34 @@ public class BasicFeature extends AbstractFeature {
     /**
      * Method description
      *
-     *
      * @param type
      */
     public void setType(String type) {
         this.type = type;
     }
 
-    public void setLink(String link){
+    public void setURL(String link) {
         this.link = link;
     }
 
-    public String getLink(){
-        return link;   
+    public String getURL() {
+        return link;
+    }
+
+
+    public int getThickEnd() {
+        return thickEnd;
+    }
+
+    public void setThickEnd(int thickEnd) {
+        this.thickEnd = thickEnd;
+    }
+
+    public int getThickStart() {
+        return thickStart;
+    }
+
+    public void setThickStart(int thickStart) {
+        this.thickStart = thickStart;
     }
 }
diff --git a/src/org/broad/igv/feature/Bed4Feature.java b/src/org/broad/igv/feature/Bed4Feature.java
new file mode 100644
index 0000000..b5642fb
--- /dev/null
+++ b/src/org/broad/igv/feature/Bed4Feature.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.feature;
+
+import org.broad.igv.track.WindowFunction;
+
+import java.awt.*;
+import java.util.*;
+
+
+public class Bed4Feature implements Feature {
+
+    private String chr;
+    private int start;
+    private int end;
+    private Strand strand = Strand.NONE;
+
+    public String getType() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public String getChr() {
+        return chr;
+    }
+
+    public void setChr(String chr) {
+        this.chr = chr;
+    }
+
+    public int getStart() {
+        return start;
+    }
+
+    public void setStart(int start) {
+        this.start = start;
+    }
+
+    public int getEnd() {
+        return end;
+    }
+
+    public String getIdentifier() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public String getName() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public String getDescription() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean hasScore() {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public void setEnd(int end) {
+        this.end = end;
+    }
+
+    public float getScore() {
+        return 0;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public void setConfidence(float confidence) {
+        //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public float getConfidence() {
+        return 0;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public LocusScore copy() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public String getValueString(double position, WindowFunction windowFunction) {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public Strand getStrand() {
+        return strand;
+    }
+
+    public boolean hasStrand() {
+        return strand != Strand.NONE;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public void setName(String name) {
+        //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public Color getColor() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean contains(Feature feature) {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public java.util.List<Exon> getExons() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public int getLength() {
+        return 0;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public Map<String, String> getAttributes() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean contains(double location) {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public String getURL() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public void setStrand(Strand strand) {
+        this.strand = strand;
+    }
+}
diff --git a/src/org/broad/igv/feature/Chromosome.java b/src/org/broad/igv/feature/Chromosome.java
index 62de81a..783a66d 100644
--- a/src/org/broad/igv/feature/Chromosome.java
+++ b/src/org/broad/igv/feature/Chromosome.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/feature/CommandLineGenomeBuilder.java b/src/org/broad/igv/feature/CommandLineGenomeBuilder.java
index 47baeb4..b458824 100644
--- a/src/org/broad/igv/feature/CommandLineGenomeBuilder.java
+++ b/src/org/broad/igv/feature/CommandLineGenomeBuilder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,7 +23,7 @@
 
 package org.broad.igv.feature;
 
-import org.broad.igv.IGVConstants;
+import org.broad.igv.Globals;
 
 import java.util.HashMap;
 import java.util.HashSet;
@@ -120,9 +120,9 @@ public class CommandLineGenomeBuilder {
 
         String genomeFileName = null;
         if (genomeArchiveFilenamePrefix != null) {
-            genomeFileName = genomeArchiveFilenamePrefix + IGVConstants.GENOME_FILE_EXTENSION;
+            genomeFileName = genomeArchiveFilenamePrefix + Globals.GENOME_FILE_EXTENSION;
         } else {
-            genomeFileName = genomeId + IGVConstants.GENOME_FILE_EXTENSION;
+            genomeFileName = genomeId + Globals.GENOME_FILE_EXTENSION;
         }
 
         String relativeSequenceLocation = null;
diff --git a/src/org/broad/igv/feature/CytoBandFileParser.java b/src/org/broad/igv/feature/CytoBandFileParser.java
index e0038a6..a56fc76 100644
--- a/src/org/broad/igv/feature/CytoBandFileParser.java
+++ b/src/org/broad/igv/feature/CytoBandFileParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -36,7 +36,7 @@ import java.util.LinkedHashMap;
  */
 public class CytoBandFileParser {
 
-    private static Logger logger = Logger.getLogger(CytoBandFileParser.class);
+    private static Logger log = Logger.getLogger(CytoBandFileParser.class);
 
 
     /**
@@ -64,19 +64,16 @@ public class CytoBandFileParser {
 
         }
         catch (Exception e) {
-            logger.error("Invalid Cytoband file data : file=" + filename, e);
+            log.error("Invalid Cytoband file data : file=" + filename, e);
             return false;
         }
     }
 
-    /**
-     * Method description
-     *
-     * @param reader
-     * @return
-     */
+
     public static LinkedHashMap<String, Chromosome> loadData(BufferedReader reader) {
 
+        log.debug("Loading cytobands");
+
         LinkedHashMap<String, Chromosome> dataMap = new LinkedHashMap<String, Chromosome>();
         try {
 
@@ -94,12 +91,13 @@ public class CytoBandFileParser {
                 chromosome.addCytoband(cytoData);
             }
 
-            reader.close();
 
         }
         catch (IOException e) {
-            e.printStackTrace();
+            log.error("Error loading cytobands", e);
         }
+
+        log.debug("Cytobands loaded");
         return dataMap;
 
     }
diff --git a/src/org/broad/igv/feature/Cytoband.java b/src/org/broad/igv/feature/Cytoband.java
index e9b7103..27eb3d2 100644
--- a/src/org/broad/igv/feature/Cytoband.java
+++ b/src/org/broad/igv/feature/Cytoband.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/feature/EmblFeatureTableParser.java b/src/org/broad/igv/feature/EmblFeatureTableParser.java
index 2cc2321..f753cd8 100644
--- a/src/org/broad/igv/feature/EmblFeatureTableParser.java
+++ b/src/org/broad/igv/feature/EmblFeatureTableParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -21,10 +21,12 @@ package org.broad.igv.feature;
 
 import org.broad.igv.renderer.BasicFeatureRenderer;
 import org.broad.igv.renderer.GeneTrackRenderer;
+import org.broad.igv.track.FeatureCollectionSource;
 import org.broad.igv.track.FeatureTrack;
+import org.broad.igv.track.TrackProperties;
 import org.broad.igv.util.AsciiLineReader;
-import org.broad.igv.util.ResourceLocator;
 import org.broad.igv.util.ParsingUtils;
+import org.broad.igv.util.ResourceLocator;
 
 import java.io.*;
 import java.util.*;
@@ -53,6 +55,10 @@ public class EmblFeatureTableParser implements FeatureParser {
         return true;
     }
 
+    public TrackProperties getTrackProperties() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
     /**
      * Method description
      *
@@ -70,9 +76,8 @@ public class EmblFeatureTableParser implements FeatureParser {
             if (features.isEmpty()) {
                 return null;
             } else {
-                String fName = locator.getDisplayName();
-                FeatureTrack track = new FeatureTrack(locator, fName, features);
-
+                FeatureTrack track = new FeatureTrack(locator, new FeatureCollectionSource(features));
+                track.setName(locator.getTrackName());
                 track.setRendererClass(BasicFeatureRenderer.class);
                 track.setMinimumHeight(35);
                 track.setHeight(45);
@@ -197,7 +202,7 @@ public class EmblFeatureTableParser implements FeatureParser {
                     newFeatureList.add(gene);
                     genes.put(geneId, gene);
                 }
-                Exon exon = new Exon(feature.getChromosome(), feature.getStart(), feature.getEnd(),
+                Exon exon = new Exon(feature.getChr(), feature.getStart(), feature.getEnd(),
                         feature.getStrand());
                 gene.addExon(exon);
             }
@@ -212,7 +217,7 @@ public class EmblFeatureTableParser implements FeatureParser {
             } else if (type.equals("3'UTR") || type.equals("5'UTR")) {
                 BasicFeature gene = genes.get(feature.getIdentifier());
                 if (gene != null) {
-                    Exon exon = new Exon(feature.getChromosome(), feature.getStart(),
+                    Exon exon = new Exon(feature.getChr(), feature.getStart(),
                             feature.getEnd(), feature.getStrand());
                     exon.setUTR(true);
                     gene.addExon(exon);
diff --git a/src/org/broad/igv/feature/Exon.java b/src/org/broad/igv/feature/Exon.java
index 3cb4f7c..e1e7e80 100644
--- a/src/org/broad/igv/feature/Exon.java
+++ b/src/org/broad/igv/feature/Exon.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -21,8 +21,8 @@ package org.broad.igv.feature;
 
 //~--- non-JDK imports --------------------------------------------------------
 
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.track.WindowFunction;
-import org.broad.igv.ui.IGVModel;
 
 //~--- JDK imports ------------------------------------------------------------
 
@@ -78,14 +78,6 @@ public class Exon extends AbstractFeature {
         }
     }
 
-    /**
-     * Constructs ...
-     *
-     * @param chr
-     * @param start
-     * @param end
-     * @param strand
-     */
     public Exon(String chr, int start, int end, Strand strand) {
         super(chr, start, end, strand);
 
@@ -182,7 +174,7 @@ public class Exon extends AbstractFeature {
      *
      * @return
      */
-    int getReadingShift() {
+    public int getReadingShift() {
         return readingFrame;
     }
 
@@ -215,20 +207,19 @@ public class Exon extends AbstractFeature {
         }
         int start = getStart();
         int end = getEnd();
-        String chr = getChromosome();
+        String chr = getChr();
         if (readingFrame >= 0) {
             int readStart = (codingStart > start) ? codingStart : start + readingFrame;
             int readEnd = Math.min(end, codingEnd);
             if (readEnd > readStart + 3) {
-                String genome = IGVModel.getInstance().getViewContext().getGenomeId();
-                aminoAcidSequence = AminoAcidManager.getAminoAcidSequence(genome, chr, readStart,
-                        readEnd, getStrand());
+                String genome = ViewContext.getInstance().getGenomeId();
+                aminoAcidSequence = AminoAcidManager.getAminoAcidSequence(genome, chr, readStart, readEnd, getStrand());
             }
         }
     }
 
     public LocusScore copy() {
-        Exon copy = new Exon(getChromosome(), getStart(), getEnd(), getStrand());
+        Exon copy = new Exon(getChr(), getStart(), getEnd(), getStrand());
         copy.aminoAcidSequence = this.aminoAcidSequence;
         copy.codingEnd = this.codingEnd;
         copy.codingStart = this.codingStart;
@@ -241,6 +232,7 @@ public class Exon extends AbstractFeature {
         if (aaNumber > 0) {
             msg += "<br>Amino acid number: " + aaNumber;
         }
+        if (description != null) msg +=  description;
         return msg;
     }
 
@@ -252,4 +244,11 @@ public class Exon extends AbstractFeature {
         this.number = number;
     }
 
+    public String getURL() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean isUTR() {
+        return utr;
+    }
 }
diff --git a/src/org/broad/igv/feature/Feature.java b/src/org/broad/igv/feature/Feature.java
index 6e41081..46994bf 100644
--- a/src/org/broad/igv/feature/Feature.java
+++ b/src/org/broad/igv/feature/Feature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,108 +23,37 @@ import java.awt.*;
 import java.util.List;
 import java.util.Map;
 
-/**
- * Interface description
- *
- * @author Enter your name here...
- * @version Enter version here..., 08/10/16
- */
-public interface Feature extends LocusScore {
 
+public interface Feature extends LocusScore {
 
     public String getType();
 
+    public String getChr();
+
+    public void setChr(String chr);
+
+    public int getStart();
+
+    public int getEnd();
+
     public String getIdentifier();
 
     public String getName();
 
-    /**
-     * Method description
-     *
-     *
-     * @return
-     */
     public String getDescription();
 
-
-    /**
-     * Method description
-     *
-     *
-     * @return
-     */
     public boolean hasScore();
 
-    /**
-     * Method description
-     *
-     *
-     * @return
-     */
     public Strand getStrand();
 
-    /**
-     * Method description
-     *
-     *
-     * @return
-     */
     public boolean hasStrand();
 
-
-    public void setChromosome(String chr);
-
     public void setName(String name);
 
-    /**
-     * Method description
-     *
-     *
-     * @return
-     */
-    public String getChromosome();
-
-    /**
-     * Method description
-     *
-     *
-     * @return
-     */
     public Color getColor();
 
-    /**
-     * Method description
-     *
-     *
-     * @return
-     */
-    public int getStart();
-
-    /**
-     * Method description
-     *
-     *
-     * @return
-     */
-
-    public int getEnd();
-
-    /**
-     * Method description
-     *
-     *
-     * @param feature
-     *
-     * @return
-     */
     public boolean contains(Feature feature);
 
-    /**
-     * Method description
-     *
-     *
-     * @return
-     */
     public List<Exon> getExons();
 
     public int getLength();
@@ -133,6 +62,6 @@ public interface Feature extends LocusScore {
 
     boolean contains(double location);
 
-
+    public String getURL();
 
 }
diff --git a/src/org/broad/igv/feature/FeatureDB.java b/src/org/broad/igv/feature/FeatureDB.java
index 41118c7..025775f 100644
--- a/src/org/broad/igv/feature/FeatureDB.java
+++ b/src/org/broad/igv/feature/FeatureDB.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -20,8 +20,8 @@ package org.broad.igv.feature;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
-import org.broad.igv.ui.IGVModel;
+import org.broad.igv.Globals;
+import org.broad.igv.session.ViewContext;
 
 import java.util.HashMap;
 import java.util.List;
@@ -43,21 +43,21 @@ public class FeatureDB {
 
     public static void addFeature(Feature feature) {
 
-        if (IGVConstants.isHeadless()) {
+        if (Globals.isHeadless()) {
             return;
         }
 
-        Genome currentGenome = IGVModel.getInstance().getViewContext().getGenome();
+        Genome currentGenome = ViewContext.getInstance().getGenome();
         if (currentGenome == null) {
             return;
         }
-        if (currentGenome.getChromosome(feature.getChromosome()) != null) {
+        if (currentGenome.getChromosome(feature.getChr()) != null) {
 
             if (feature.getName() != null && feature.getName().length() > 0) {
-                featureMap.put(feature.getName().toUpperCase(), feature);
+                featureMap.put(feature.getName().trim().toUpperCase(), feature);
             }
             if (feature.getIdentifier() != null && feature.getIdentifier().length() > 0) {
-                featureMap.put(feature.getIdentifier().toUpperCase(), feature);
+                featureMap.put(feature.getIdentifier().trim().toUpperCase(), feature);
             }
         }
     }
@@ -87,14 +87,15 @@ public class FeatureDB {
     /**
      * Return the feature, if any, with the given name.  Genes are given
      * precedence.
-     *
-     * @param name
-     * @return
+
      */
-    public static Feature getFeature(String name) {
+    public static Feature getFeature(String nm) {
+
+        String name = nm.trim().toUpperCase();
+
 
         // First search genes for current genome.  This obviously needs some refactoring.
-        String currentGenome = IGVModel.getInstance().getViewContext().getGenomeId();
+        String currentGenome = ViewContext.getInstance().getGenomeId();
         GeneManager gm = GeneManager.getGeneManager(currentGenome);
         Feature gene = null;
         if (gm != null) {
diff --git a/src/org/broad/igv/feature/FeatureFileUtils.java b/src/org/broad/igv/feature/FeatureFileUtils.java
index 91cd6e2..a60afd3 100644
--- a/src/org/broad/igv/feature/FeatureFileUtils.java
+++ b/src/org/broad/igv/feature/FeatureFileUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/feature/FeatureParser.java b/src/org/broad/igv/feature/FeatureParser.java
index 40583b0..1cd77aa 100644
--- a/src/org/broad/igv/feature/FeatureParser.java
+++ b/src/org/broad/igv/feature/FeatureParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -28,6 +28,7 @@ package org.broad.igv.feature;
 
 
 import org.broad.igv.track.FeatureTrack;
+import org.broad.igv.track.TrackProperties;
 import org.broad.igv.util.AsciiLineReader;
 import org.broad.igv.util.ResourceLocator;
 
@@ -57,4 +58,6 @@ public interface FeatureParser {
      */
     public boolean isFeatureFile(ResourceLocator locator);
 
+    public TrackProperties getTrackProperties();
+
 }
diff --git a/src/org/broad/igv/feature/FeatureType.java b/src/org/broad/igv/feature/FeatureType.java
index c516a81..4bb9d5e 100644
--- a/src/org/broad/igv/feature/FeatureType.java
+++ b/src/org/broad/igv/feature/FeatureType.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/feature/FeatureUtils.java b/src/org/broad/igv/feature/FeatureUtils.java
index e632c2a..773678b 100644
--- a/src/org/broad/igv/feature/FeatureUtils.java
+++ b/src/org/broad/igv/feature/FeatureUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -33,10 +33,10 @@ public class FeatureUtils {
     public static Map<String, List<Feature>> divideByChromosome(List<Feature> features) {
         Map<String, List<Feature>> featureMap = new LinkedHashMap();
         for (Feature f : features) {
-            List<Feature> flist = featureMap.get(f.getChromosome());
+            List<Feature> flist = featureMap.get(f.getChr());
             if (flist == null) {
                 flist = new ArrayList();
-                featureMap.put(f.getChromosome(), flist);
+                featureMap.put(f.getChr(), flist);
             }
             flist.add(f);
         }
@@ -179,9 +179,25 @@ public class FeatureUtils {
 
     public static LocusScore getFeatureBefore(double position, List<? extends LocusScore> features) {
 
-        if (features.size() == 0 ||
-                features.get(features.size() - 1).getStart() <= position) {
-            return null;
+        int index = getIndexBefore(position, features);
+        while(index >= 0) {
+            LocusScore f = features.get(index);
+            if(f.getStart() < position) {
+                return f;
+            }
+            index--;
+        }
+        return null;
+       
+    }
+
+    public static int getIndexBefore(double position, List<? extends LocusScore> features) {
+
+        if (features.size() == 0 || features.get(features.size() - 1).getStart() <= position) {
+            return -1;
+        }
+        if(features.get(0).getStart() >= position) {
+            return 0;
         }
 
         int startIdx = 0;
@@ -203,19 +219,17 @@ public class FeatureUtils {
         if (features.get(endIdx).getStart() >= position) {
             for (int idx = endIdx; idx >= 0; idx--) {
                 if (features.get(idx).getStart() < position) {
-                    return features.get(idx);
+                    return idx;
                 }
             }
         } else {
             for (int idx = endIdx + 1; idx < features.size(); idx++) {
                 if (features.get(idx).getStart() >= position) {
-                    return features.get(idx - 1);
+                    return idx - 1;
                 }
 
             }
         }
-        return null;
-
-
+        return -1;
     }
 }
diff --git a/src/org/broad/igv/feature/GFFParser.java b/src/org/broad/igv/feature/GFFParser.java
index da89b9a..75c8a99 100644
--- a/src/org/broad/igv/feature/GFFParser.java
+++ b/src/org/broad/igv/feature/GFFParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -19,12 +19,14 @@
 package org.broad.igv.feature;
 
 import org.apache.log4j.Logger;
+import org.broad.igv.exceptions.DataLoadException;
 import org.broad.igv.exceptions.ParserException;
 import org.broad.igv.renderer.BasicFeatureRenderer;
 import org.broad.igv.renderer.GeneTrackRenderer;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.track.FeatureCollectionSource;
 import org.broad.igv.track.FeatureTrack;
 import org.broad.igv.track.TrackProperties;
-import org.broad.igv.ui.IGVModel;
 import org.broad.igv.util.*;
 
 import java.io.*;
@@ -45,29 +47,33 @@ public class GFFParser implements FeatureParser {
     static Logger log = Logger.getLogger(GFFParser.class);
 
     static HashSet exonTerms = new HashSet();
-    static Set<String> ignoreAttributes = new HashSet();
+    static HashSet utrTerms = new HashSet();
+    //static Set<String> ignoreAttributes = new HashSet();
     static HashSet<String> geneParts = new HashSet();
     static HashSet<String> ignoredTypes = new HashSet();
 
-    static String[] nameFields = {"Name", "name", "primary_name", "gene", "Locus", "locus", "alias", "systematic_id", "ID"};
+    static String[] nameFields = {"gene", "Name", "name", "primary_name", "Locus", "locus", "alias", "systematic_id", "ID"};
 
 
     static String[] tokens = new String[200];
 
     static {
-        exonTerms.add("five_prime_UTR");
-        exonTerms.add("three_prime_UTR");
-        exonTerms.add("5'-utr");
-        exonTerms.add("3'-utr");
-        exonTerms.add("3'-UTR");
-        exonTerms.add("5'-UTR");
-        exonTerms.add("5utr");
-        exonTerms.add("3utr");
+        utrTerms.add("five_prime_UTR");
+        utrTerms.add("three_prime_UTR");
+        utrTerms.add("5'-utr");
+        utrTerms.add("3'-utr");
+        utrTerms.add("3'-UTR");
+        utrTerms.add("5'-UTR");
+        utrTerms.add("5utr");
+        utrTerms.add("3utr");
+    }
+
+    static {
+        exonTerms.addAll(utrTerms);
         exonTerms.add("exon");
         exonTerms.add("coding_exon");
         exonTerms.add("CDS");
         exonTerms.add("cds");
-
     }
 
 
@@ -91,6 +97,7 @@ public class GFFParser implements FeatureParser {
         ignoredTypes.add("intron");
     }
 
+    /*
     static {
         ignoreAttributes.add("ID");
         ignoreAttributes.add("Parent");
@@ -101,13 +108,14 @@ public class GFFParser implements FeatureParser {
         ignoreAttributes.add("exonNumber");
 
     }
+    */
 
 
     Helper helper;
 
     Map<String, GFF3Transcript> transcriptCache = new HashMap(50000);
     Map<String, BasicFeature> geneCache = new HashMap(50000);
-    TrackProperties trackProperties = null;
+    private TrackProperties trackProperties = null;
 
     static StringBuffer buf = new StringBuffer(1000);
 
@@ -140,9 +148,13 @@ public class GFFParser implements FeatureParser {
             reader = ParsingUtils.openAsciiReader(locator);
 
             List<Feature> features = loadFeatures(reader);
+            if (features.size() == 0) {
+                throw new DataLoadException("No features were found in this file with chromosomes mapped to the current genome", locator.getPath());
+            }
 
-            FeatureTrack track = new FeatureTrack(locator, locator.getDisplayName(), features);
 
+            FeatureTrack track = new FeatureTrack(locator, new FeatureCollectionSource(features));
+            track.setName(locator.getTrackName());
             track.setRendererClass(BasicFeatureRenderer.class);
             track.setMinimumHeight(35);
             track.setHeight(45);
@@ -182,7 +194,7 @@ public class GFFParser implements FeatureParser {
         String line = null;
         try {
 
-            Genome genome = IGVModel.getInstance().getViewContext().getGenome();
+            Genome genome = ViewContext.getInstance().getGenome();
             Set<String> featuresToHide = new HashSet();
             while ((line = reader.readLine()) != null) {
 
@@ -266,14 +278,15 @@ public class GFFParser implements FeatureParser {
                     for (String pid : parentIds) {
                         getGFF3Transcript(pid).addCDSParts(chromosome, start, end, description);
                     }
-                } else if (exonTerms.contains(featureType) && parentIds != null && parentIds.length > 0) {
+                } else if (exonTerms.contains(featureType) && parentIds != null && parentIds.length > 0 &&
+                        parentIds[0] != null && parentIds[0].length() > 0 && !parentIds[0].equals(".")) {
 
-                    Exon exon = new Exon(chromosome, start, end, strand);
-                    exon.setDescription(description);
+                    String name = getName(attributes);
+                    int phase = -1;
                     String phaseString = tokens[7].trim();
                     if (!phaseString.equals(".")) {
                         try {
-                            exon.setPhase(Integer.parseInt(phaseString));
+                            phase = Integer.parseInt(phaseString);
 
                         } catch (NumberFormatException numberFormatException) {
 
@@ -282,10 +295,19 @@ public class GFFParser implements FeatureParser {
                         }
                     }
 
-                    exon.setName(getName(attributes));
+                    // Make a copy of the exon record for each parent
+                    for (String pid : parentIds) {
 
+                        Exon exon = new Exon(chromosome, start, end, strand);
+                        exon.setDescription(description);
+                        exon.setUTR(utrTerms.contains(featureType));
+
+                        if (phase >= 0) {
+                            exon.setPhase(phase);
+
+                        }
+                        exon.setName(name);
 
-                    for (String pid : parentIds) {
                         if (featureType.equals("exon")) {
                             getGFF3Transcript(pid).addExon(exon);
                         } else if (featureType.equals("CDS")) {
@@ -317,7 +339,11 @@ public class GFFParser implements FeatureParser {
 
                         if (featureType.equals("gene")) {
                             geneCache.put(id, f);
-                            features.add(f);
+                            if (featuresToHide.contains(featureType)) {
+                                FeatureDB.addFeature(f);
+                            } else {
+                                features.add(f);
+                            }
                         } else if (featureType.equals("mRNA") || featureType.equals("transcript")) {
                             String pid = null;
                             if (parentIds != null && parentIds.length > 0) {
@@ -344,10 +370,12 @@ public class GFFParser implements FeatureParser {
             FeatureDB.addFeatures(features);
 
         }
-        catch (ParserException e) {
+        catch (ParserException
+                e) {
             throw e;
         }
-        catch (Exception ex) {
+        catch (Exception
+                ex) {
             log.error("Error parsing GFF file", ex);
             if (line != null && reader.getCurrentLineNumber() != 0) {
 
@@ -367,13 +395,13 @@ public class GFFParser implements FeatureParser {
         buf.append("<br>");
         for (Map.Entry<String, String> att : attributes.entrySet()) {
 
-            if (!ignoreAttributes.contains(att.getKey())) {
-                String attValue = att.getValue().replaceAll(";", "<br>");
-                buf.append(att.getKey());
-                buf.append(" = ");
-                buf.append(attValue);
-                buf.append("<br>");
-            }
+            //if (!ignoreAttributes.contains(att.getKey())) {
+            String attValue = att.getValue().replaceAll(";", "<br>");
+            buf.append(att.getKey());
+            buf.append(" = ");
+            buf.append(attValue);
+            buf.append("<br>");
+            //}
         }
         String description = buf.toString();
 
@@ -402,22 +430,23 @@ public class GFFParser implements FeatureParser {
 
 
     String getName(Map<String, String> attributes) {
+
+        if (attributes.size() == 0) {
+            return null;
+        }
+
         for (String nf : nameFields) {
             if (attributes.containsKey(nf)) {
                 return attributes.get(nf);
             }
         }
 
-        // Try "note" as a last resort
-        if (attributes.containsKey("note")) {
-            int nTokens = ParsingUtils.split(attributes.get("note"), tokens, ',');
-            return nTokens < 1 ? "" : tokens[0];
-        }
-        return null;
+        return attributes.values().iterator().next();
+
     }
 
 
-    static void splitFileByType(String gffFile, String outputDirectory) throws IOException {
+    public static void splitFileByType(String gffFile, String outputDirectory) throws IOException {
 
         BufferedReader br = new BufferedReader(new FileReader(gffFile));
         String nextLine;
@@ -435,8 +464,8 @@ public class GFFParser implements FeatureParser {
                 }
                 if (!writers.containsKey(type)) {
                     writers.put(type,
-                                new PrintWriter(new FileWriter(new File(outputDirectory,
-                                                                        type + ".gff"))));
+                            new PrintWriter(new FileWriter(new File(outputDirectory,
+                                    type + ".gff"))));
                 }
             }
         }
@@ -476,6 +505,10 @@ public class GFFParser implements FeatureParser {
         }
     }
 
+    public TrackProperties getTrackProperties() {
+        return trackProperties;
+    }
+
     class GFF3Transcript {
 
         private String id;
@@ -561,24 +594,26 @@ public class GFFParser implements FeatureParser {
             if (cdsList == null) {
                 cdsList = cdss;
             }
-            while (!cdsList.isEmpty()) {
-                Exon cds = cdsList.get(0);
-                Exon exon = findMatchingExon(cds);
-                if (exon == null) {
-                    exons.add(cds);
-                } else {
-                    exon.setCodingStart(cds.getStart());
-                    exon.setCodingEnd(cds.getEnd());
-                    exon.setReadingFrame(cds.getReadingShift());
+            if (exons.size() > 0) {
+                while (!cdsList.isEmpty()) {
+                    Exon cds = cdsList.get(0);
+                    Exon exon = findMatchingExon(cds);
+                    if (exon == null) {
+                        exons.add(cds);
+                    } else {
+                        exon.setCodingStart(cds.getStart());
+                        exon.setCodingEnd(cds.getEnd());
+                        exon.setReadingFrame(cds.getReadingShift());
+                    }
+                    cdsList.remove(0);
+                }
+                for (Exon exon : exons) {
+                    chr = exon.getChr();
+                    strand = exon.getStrand();
+                    start = Math.min(exon.getStart(), start);
+                    end = Math.max(exon.getEnd(), end);
+                    name = exon.getName();
                 }
-                cdsList.remove(0);
-            }
-            for (Exon exon : exons) {
-                chr = exon.getChromosome();
-                strand = exon.getStrand();
-                start = Math.min(exon.getStart(), start);
-                end = Math.max(exon.getEnd(), end);
-                name = exon.getName();
             }
 
             if (transcript == null) {
@@ -591,7 +626,6 @@ public class GFFParser implements FeatureParser {
 
             if ((parentId != null) && geneCache.containsKey(parentId)) {
                 BasicFeature gene = geneCache.get(parentId);
-                geneCache.remove(parentId);
                 if (transcript.getName() == null && gene.getName() != null) {
                     transcript.setName(gene.getName());
                 }
@@ -663,7 +697,7 @@ public class GFFParser implements FeatureParser {
 
     class GFF2Helper implements Helper {
 
-        String[] idFields = {"systematic_id", "ID", "Name", "name", "primary_name", "gene", "Locus", "locus", "alias"};
+        String[] idFields = {"systematic_id", "ID", "transcript_id", "Name", "name", "primary_name", "gene", "Locus", "locus", "alias"};
 
 
         public void setUrlDecoding(boolean b) {
diff --git a/src/org/broad/igv/feature/GeneManager.java b/src/org/broad/igv/feature/GeneManager.java
index 5734fdd..8a7e704 100644
--- a/src/org/broad/igv/feature/GeneManager.java
+++ b/src/org/broad/igv/feature/GeneManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -26,6 +26,8 @@ package org.broad.igv.feature;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
+import org.broad.igv.track.TrackProperties;
+import org.broad.igv.ui.util.MessageUtils;
 import org.broad.igv.util.AsciiLineReader;
 import org.broad.igv.util.ResourceLocator;
 
@@ -55,10 +57,8 @@ public class GeneManager {
      * Map of chromosome -> longest gene
      */
     Map<String, Feature> longestGeneMap;
-    /**
-     * Set of loaded gene variants.  Used to prevent loading of duplicates.
-     */
-    Set<String> loadedGeneKeys;
+
+    private TrackProperties trackProperties;
 
     public GeneManager(String genomeId) {
         this(genomeId, "Gene");
@@ -78,7 +78,6 @@ public class GeneManager {
         geneMap = new HashMap<String, Feature>();
         maxLocationMap = new HashMap<String, Integer>();
         longestGeneMap = new HashMap<String, Feature>();
-        loadedGeneKeys = new HashSet();
         this.geneTrackName = geneTrackName;
     }
 
@@ -93,63 +92,57 @@ public class GeneManager {
      */
     public void addGene(Feature gene) {
 
-        String key = gene.getIdentifier();
-
-        if (!loadedGeneKeys.contains(key)) {
-            loadedGeneKeys.add(key);
+        // If there is a genome associated with this manager only include
+        // genes whose chromosome is contained in the genome.
 
-            // If there is a genome associated with this manager only include
-            // genes whose chromosome is contained in the genome.
-
-            if (genome != null) {
-                if (genome.getChromosome(gene.getChromosome()) == null) {
-                    return;
-                }
+        if (genome != null) {
+            if (genome.getChromosome(gene.getChr()) == null) {
+                return;
             }
+        }
 
-            // If there are multiple variant of a gene, use the longest
-            Feature currentGene = geneMap.get(gene.getName());
-            if (gene.getIdentifier() != null) {
-                geneMap.put(gene.getIdentifier().trim().toUpperCase(), gene);
+        // If there are multiple variant of a gene, use the longest
+        Feature currentGene = geneMap.get(gene.getName());
+        if (gene.getIdentifier() != null) {
+            geneMap.put(gene.getIdentifier().trim().toUpperCase(), gene);
+        }
+        if (currentGene == null) {
+            if (gene.getName() != null) {
+                geneMap.put(gene.getName().trim().toUpperCase(), gene);
             }
-            if (currentGene == null) {
+        } else {
+            int w1 = currentGene.getEnd() - currentGene.getStart();
+            int w2 = gene.getEnd() - gene.getStart();
+            if (w2 > w1) {
                 if (gene.getName() != null) {
                     geneMap.put(gene.getName().trim().toUpperCase(), gene);
                 }
-            } else {
-                int w1 = currentGene.getEnd() - currentGene.getStart();
-                int w2 = gene.getEnd() - gene.getStart();
-                if (w2 > w1) {
-                    if (gene.getName() != null) {
-                        geneMap.put(gene.getName().trim().toUpperCase(), gene);
-                    }
-                }
             }
+        }
 
 
-            // Update "longest gene" for this chromosome
-            String chr = gene.getChromosome();
-            List<Feature> geneDataList = chromosomeGeneMap.get(chr);
-            if (geneDataList == null) {
-                geneDataList = new ArrayList<Feature>();
-                chromosomeGeneMap.put(chr, geneDataList);
-                maxLocationMap.put(chr, (int) gene.getEnd());
-            }
-            if (gene.getEnd() > maxLocationMap.get(chr)) {
-                maxLocationMap.put(chr, (int) gene.getEnd());
-            }
+        // Update "longest gene" for this chromosome
+        String chr = gene.getChr();
+        List<Feature> geneDataList = chromosomeGeneMap.get(chr);
+        if (geneDataList == null) {
+            geneDataList = new ArrayList<Feature>();
+            chromosomeGeneMap.put(chr, geneDataList);
+            maxLocationMap.put(chr, (int) gene.getEnd());
+        }
+        if (gene.getEnd() > maxLocationMap.get(chr)) {
+            maxLocationMap.put(chr, (int) gene.getEnd());
+        }
 
-            if (longestGeneMap.get(chr) == null) {
+        if (longestGeneMap.get(chr) == null) {
+            longestGeneMap.put(chr, gene);
+        } else {
+            if (gene.getLength() > longestGeneMap.get(chr).getLength()) {
                 longestGeneMap.put(chr, gene);
-            } else {
-                if (gene.getLength() > longestGeneMap.get(chr).getLength()) {
-                    longestGeneMap.put(chr, gene);
-                }
             }
-
-            geneDataList.add((Feature) gene);
         }
 
+        geneDataList.add(gene);
+
     }
 
     /**
@@ -270,15 +263,22 @@ public class GeneManager {
                         geneManager = new GeneManager(genomeId, genomeDescriptor.getGeneTrackName());
                         String geneFilename = genomeDescriptor.getGeneFileName();
                         FeatureParser parser = AbstractFeatureParser.getInstanceFor(new ResourceLocator(geneFilename));
-                        List<Feature> genes = parser.loadFeatures(reader);
-                        for (Feature gene : genes) {
-                            geneManager.addGene(gene);
+                        if (parser == null) {
+                            MessageUtils.showMessage("ERROR: Unrecognized annotation file format: " + geneFilename +
+                                    "<br>Annotations for genome: " + genomeId + " will not be loaded.");
+                        } else {
+                            List<Feature> genes = parser.loadFeatures(reader);
+                            for (Feature gene : genes) {
+                                geneManager.addGene(gene);
+                            }
+
+                            geneManager.sortGeneLists();
+
+                            geneManager.trackProperties = parser.getTrackProperties();
                         }
-
-                        geneManager.sortGeneLists();
-
                         geneManagerCache.put(genomeId, geneManager);
-                    } catch(Exception e)  {
+
+                    } catch (Exception e) {
                         log.error("Error loading geneManager", e);
                     }
                     finally {
@@ -375,4 +375,12 @@ public class GeneManager {
         }
         return false;
     }
+
+    /**
+     * Any track properties recorded in the annotation file defining the genes.  This is an odd place for this,
+     * but its neccessary to record it here as this class initiales parsing of the file.
+     */
+    public TrackProperties getTrackProperties() {
+        return trackProperties;
+    }
 }
diff --git a/src/org/broad/igv/feature/Genome.java b/src/org/broad/igv/feature/Genome.java
index d1cf003..355c59b 100644
--- a/src/org/broad/igv/feature/Genome.java
+++ b/src/org/broad/igv/feature/Genome.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -27,33 +27,46 @@
 */
 package org.broad.igv.feature;
 
-//~--- JDK imports ------------------------------------------------------------
 
-import org.broad.igv.IGVConstants;
+import org.broad.igv.Globals;
 
 import java.util.*;
 
 /**
- * Simple model of a genome, essesntially a collection of cytobands.
+ * Simple model of a genome
  */
 public class Genome {
 
     private Map<String, String> chrAliasTable;
 
     private String id;
+    private String formatVersion;
     private LinkedHashMap<String, Chromosome> chromosomeMap;
-    List<String> chromosomeNames;
-    boolean chromosomesAreOrdered = false;
-    long length = -1;
+    private List<String> chromosomeNames;
+    private long length = -1;
+    private String annotationURL;
+    private Map<String, Long> cumulativeOffsets = new HashMap();
+    public static final int MAX_WHOLE_GENOME = 10000;
+
 
-    /**
-     * Creates a new instance of Genome
-     *
-     * @param id
-     */
     public Genome(String id) {
         this.id = id;
         createAliasTable();
+        initAnnotationURL();
+    }
+
+    /**
+     * A temporary implementation until this is supported in the genome descriptor file
+     */
+    static Set<String> ucscGenomes = new HashSet(Arrays.asList("hg16", "hg17", "hg18", "hg19", "mm7",
+            "mm8", "mm9", "sacCer1", "sacCer2", "ce6", "canFam2", "monDom5"));
+
+    private void initAnnotationURL() {
+        if (id != null) {
+            if (ucscGenomes.contains(id)) {
+                annotationURL = "http://genome.ucsc.edu/cgi-bin/hgGene?hgg_gene=$$&db=" + id;
+            }
+        }
     }
 
 
@@ -62,43 +75,42 @@ public class Genome {
      * TODO -- this should be part of the genome definition file
      */
     private void createAliasTable() {
-        chrAliasTable = new HashMap(100);
-        if (id.startsWith("hg") || id.equalsIgnoreCase("1kg_ref")) {
 
-            for (int i = 0; i < 23; i++) {
-                chrAliasTable.put(String.valueOf(i), "chr" + i);
-                //chrLookupTable.put("Chr" + i, "chr" + i);
-            }
+        chrAliasTable = GenomeManager.getInstance().getChromosomeAliasTable(id);
+
+        if (chrAliasTable == null) {
+            chrAliasTable = new HashMap(100);
+        }
+        if (id.startsWith("hg") || id.equalsIgnoreCase("1kg_ref")) {
             chrAliasTable.put("23", "chrX");
             chrAliasTable.put("24", "chrY");
-            chrAliasTable.put("X", "chrX");
-            chrAliasTable.put("Y", "chrY");
-            chrAliasTable.put("M", "chrM");
+            chrAliasTable.put("chr23", "chrX");
+            chrAliasTable.put("chr24", "chrY");
             chrAliasTable.put("MT", "chrM");
         } else if (id.startsWith("mm")) {
-
-            for (int i = 0; i < 21; i++) {
-                chrAliasTable.put(String.valueOf(i), "chr" + i);
-                //chrLookupTable.put("Chr" + i, "chr" + i);
-            }
             chrAliasTable.put("21", "chrX");
             chrAliasTable.put("22", "chrY");
-            chrAliasTable.put("X", "chrX");
-            chrAliasTable.put("Y", "chrY");
-            chrAliasTable.put("M", "chrM");
+            chrAliasTable.put("chr21", "chrX");
+            chrAliasTable.put("chr22", "chrY");
             chrAliasTable.put("MT", "chrM");
+        } else if (id.equals("b37")) {
+            chrAliasTable.put("chrM", "MT");
+            chrAliasTable.put("chrX", "23");
+            chrAliasTable.put("chrY", "24");
+
         }
-        // TODO -- search for user specfied mappings and read them in.
     }
 
 
     public String getChromosomeAlias(String str) {
-        String chr = chrAliasTable.get(str);
-        if (chr == null) {
-            chr = str;
-            chrAliasTable.put(str, chr);
+        if (chrAliasTable == null) {
+            return str;
+        } else {
+            if (chrAliasTable.containsKey(str)) {
+                return chrAliasTable.get(str);
+            }
         }
-        return chr;
+        return str;
     }
 
 
@@ -108,14 +120,27 @@ public class Genome {
         if (!chromosomesAreOrdered) {
             Collections.sort(chromosomeNames, new ChromosomeComparator());
         }
+        // Update the chromosome alias table with common variations -- don't do for genomes with large #s of scallfolds
+        if (chromosomeNames.size() < 1000) {
+            if (chrAliasTable == null) {
+                chrAliasTable = new HashMap();
+            }
+            for (String name : chromosomeNames) {
+                if (name.startsWith("chr") || name.startsWith("Chr")) {
+                    chrAliasTable.put(name.substring(3), name);
+                } else {
+                    chrAliasTable.put("chr" + name, name);
+                }
+            }
+        }
     }
 
+
     public String getHomeChromosome() {
-        if (getChromosomeNames().size() == 1 ||
-                getChromosomeNames().size() > 500) {
+        if (getChromosomeNames().size() == 1 || chromosomeNames.size() > MAX_WHOLE_GENOME) {
             return getChromosomeNames().get(0);
         } else {
-            return IGVConstants.CHR_ALL;
+            return Globals.CHR_ALL;
         }
     }
 
@@ -146,8 +171,6 @@ public class Genome {
     }
 
 
-    Map<String, Long> cumulativeOffsets = new HashMap();
-
     public long getCumulativeOffset(String chr) {
 
         Long cumOffset = cumulativeOffsets.get(chr);
@@ -166,7 +189,7 @@ public class Genome {
     }
 
     /**
-     * Return the chromosome coordinate in BP in genome coordinates in KBP
+     * Covert the chromosome coordinate in BP to genome coordinates in KBP
      *
      * @param chr
      * @param locationBP
@@ -177,6 +200,26 @@ public class Genome {
     }
 
     /**
+     * Convert the genome coordinates in KBP to a chromosome coordinate
+     */
+    public ChromosomeCoordinate getChromosomeCoordinate(int genomeKBP) {
+
+        long cumOffset = 0;
+        for (String c : chromosomeNames) {
+            int chrLen = getChromosome(c).getLength();
+            if ((cumOffset + chrLen) / 1000 > genomeKBP) {
+                int bp = (int) (genomeKBP * 1000 - cumOffset);
+                return new ChromosomeCoordinate(c, bp);
+            }
+            cumOffset += chrLen;
+        }
+
+        String c = chromosomeNames.get(chromosomeNames.size() - 1);
+        int bp = (int) (genomeKBP - cumOffset) * 1000;
+        return new ChromosomeCoordinate(c, bp);
+    }
+
+    /**
      * Method description
      *
      * @return
@@ -186,6 +229,29 @@ public class Genome {
     }
 
 
+    public String getAnnotationURL() {
+        return annotationURL;
+    }
+
+
+    public static class ChromosomeCoordinate {
+        private String chr;
+        private int coordinate;
+
+        public ChromosomeCoordinate(String chr, int coordinate) {
+            this.chr = chr;
+            this.coordinate = coordinate;
+        }
+
+        public String getChr() {
+            return chr;
+        }
+
+        public int getCoordinate() {
+            return coordinate;
+        }
+    }
+
     /**
      * Comparator for chromosome names.
      */
@@ -201,10 +267,9 @@ public class Genome {
             try {
 
                 // Special rule -- put the mitochondria at the end
-                if(chr1.equals("chrM") || chr1.equals("MT")) {
+                if (chr1.equals("chrM") || chr1.equals("MT")) {
                     return 1;
-                }
-                else if(chr2.equals("chrM") || chr2.equals("MT")) {
+                } else if (chr2.equals("chrM") || chr2.equals("MT")) {
                     return -1;
                 }
 
diff --git a/src/org/broad/igv/feature/GenomeDescriptor.java b/src/org/broad/igv/feature/GenomeDescriptor.java
index 0c7160f..bc7bb6f 100644
--- a/src/org/broad/igv/feature/GenomeDescriptor.java
+++ b/src/org/broad/igv/feature/GenomeDescriptor.java
@@ -96,6 +96,9 @@ public abstract class GenomeDescriptor {
         return isFileGZipFormat(fileName);
     }
 
+    public void setSequenceLocation(String sequenceLocation) {
+        this.sequenceLocation = sequenceLocation;
+    }
 
     public String getSequenceLocation() {
         return sequenceLocation;
diff --git a/src/org/broad/igv/feature/GenomeException.java b/src/org/broad/igv/feature/GenomeException.java
index b5b79cf..aa0f95e 100644
--- a/src/org/broad/igv/feature/GenomeException.java
+++ b/src/org/broad/igv/feature/GenomeException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/feature/GenomeImporter.java b/src/org/broad/igv/feature/GenomeImporter.java
index 87ddaee..d2f3f3d 100644
--- a/src/org/broad/igv/feature/GenomeImporter.java
+++ b/src/org/broad/igv/feature/GenomeImporter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,8 +23,9 @@
 package org.broad.igv.feature;
 
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
-import static org.broad.igv.IGVConstants.*;
+import org.broad.igv.Globals;
+
+import org.broad.igv.ui.UIConstants;
 import org.broad.igv.ui.util.ProgressMonitor;
 import org.broad.igv.util.AsciiLineReader;
 import org.broad.igv.util.FileUtils;
@@ -43,7 +44,7 @@ import java.util.zip.ZipInputStream;
  * @author jrobinso
  */
 public class GenomeImporter {
-    public static final int MAX_CONTIGS = 200000;
+    public static final int MAX_CONTIGS = 1000000;
 
     static Logger log = Logger.getLogger(GenomeImporter.class);
 
@@ -91,7 +92,6 @@ public class GenomeImporter {
         boolean chromsSorted = false;
         boolean altererdChrFilenames = false;
 
-
         File archive = null;
         FileWriter propertyFileWriter = null;
         try {
@@ -110,15 +110,17 @@ public class GenomeImporter {
                 if (sequenceInputFile.isDirectory()) {
                     // FASTA directory
                     // Create all sequence files
-                    File[] files = sequenceInputFile.listFiles();
-                    int progressIncrement = ((files.length > 0) ? (50 / files.length) : 50);
+                    List<File> files = getSequenceFiles(sequenceInputFile);
+                    int progressIncrement = ((files.size() > 0) ? (50 / files.size()) : 50);
                     for (File file : files) {
-                        altererdChrFilenames = createSequenceFiles(file, sequenceOutputFolder, genomeId, chromSizes, monitor);
-                        if (monitor != null) {
-                            monitor.fireProgressChange(progressIncrement);
+                        if (!file.getName().startsWith(".")) {
+                            altererdChrFilenames = createSequenceFiles(file, sequenceOutputFolder, genomeId, chromSizes, monitor);
+                            if (monitor != null) {
+                                monitor.fireProgressChange(progressIncrement);
+                            }
                         }
                     }
-                } else if (sequenceInputFile.getName().toLowerCase().endsWith(IGVConstants.ZIP_EXTENSION)) {
+                } else if (sequenceInputFile.getName().toLowerCase().endsWith(Globals.ZIP_EXTENSION)) {
                     // Zip of fastas
                     ZipArchiveWrapper zip = new ZipArchiveWrapper(sequenceInputFile);
                     ZipIterator iterator = null;
@@ -159,6 +161,7 @@ public class GenomeImporter {
 
                 // Create Cytoband file
                 if (autoGeneratedCytobandFile) {
+                    File IGV_TEMP_DIRECTORY = UIConstants.getTmpDirectory();
                     // Construct a cytoband file
                     String cytobandFileName = genomeId + "_cytoband.txt";
                     cytobandFile = new File(IGV_TEMP_DIRECTORY, cytobandFileName);
@@ -196,6 +199,19 @@ public class GenomeImporter {
         return archive;
     }
 
+    private List<File> getSequenceFiles(File sequenceDir) {
+        ArrayList<File> files = new ArrayList();
+        for (File f : sequenceDir.listFiles()) {
+            if (f.isDirectory()) {
+                files.addAll(getSequenceFiles(f));
+            } else {
+                files.add(f);
+            }
+        }
+
+        return files;
+    }
+
 
     private void generateCytobandFile(Map<String, Integer> chromSizes, File cytobandFile, boolean singleFasta) throws IOException {
 
@@ -245,10 +261,9 @@ public class GenomeImporter {
                                          boolean chromsSorted,
                                          boolean alteredChrFilenames) throws IOException {
 
-
         PrintWriter propertyFileWriter = null;
         try {
-
+            File IGV_TEMP_DIRECTORY = UIConstants.getTmpDirectory();
             File propertyFile = new File(IGV_TEMP_DIRECTORY, "property.txt");
             propertyFile.createNewFile();
 
@@ -261,22 +276,22 @@ public class GenomeImporter {
 
             propertyFileWriter.println("ordered=" + String.valueOf(chromsSorted));
             if (genomeId != null) {
-                propertyFileWriter.println(GENOME_ARCHIVE_ID_KEY + "=" + genomeId);
+                propertyFileWriter.println(Globals.GENOME_ARCHIVE_ID_KEY + "=" + genomeId);
             }
             if (genomeDisplayName != null) {
-                propertyFileWriter.println(GENOME_ARCHIVE_NAME_KEY + "=" + genomeDisplayName);
+                propertyFileWriter.println(Globals.GENOME_ARCHIVE_NAME_KEY + "=" + genomeDisplayName);
             }
             if (cytobandFile != null) {
-                propertyFileWriter.println(GENOME_ARCHIVE_CYTOBAND_FILE_KEY + "=" + cytobandFile.getName());
+                propertyFileWriter.println(Globals.GENOME_ARCHIVE_CYTOBAND_FILE_KEY + "=" + cytobandFile.getName());
             }
             if (refFlatFile != null) {
-                propertyFileWriter.println(GENOME_ARCHIVE_GENE_FILE_KEY + "=" + refFlatFile.getName());
+                propertyFileWriter.println(Globals.GENOME_ARCHIVE_GENE_FILE_KEY + "=" + refFlatFile.getName());
             }
             if (relativeSequenceLocation != null) {
-                if (!relativeSequenceLocation.startsWith("http:")) {
+                if (!(relativeSequenceLocation.startsWith("http:") || relativeSequenceLocation.startsWith("https:"))) {
                     relativeSequenceLocation = relativeSequenceLocation.replace('\\', '/');
                 }
-                propertyFileWriter.println(GENOME_ARCHIVE_SEQUENCE_FILE_LOCATION_KEY + "=" + relativeSequenceLocation);
+                propertyFileWriter.println(Globals.GENOME_ARCHIVE_SEQUENCE_FILE_LOCATION_KEY + "=" + relativeSequenceLocation);
             }
             return propertyFile;
 
@@ -354,7 +369,7 @@ public class GenomeImporter {
 
         InputStream inputStream = null;
         try {
-            if (sequenceInputFile.getName().toLowerCase().endsWith(FASTA_GZIP_FILE_EXTENSION)) {
+            if (sequenceInputFile.getName().toLowerCase().endsWith(Globals.FASTA_GZIP_FILE_EXTENSION)) {
 
                 // A single FASTA file is in a .gz file
                 inputStream = new GZIPInputStream(new FileInputStream(sequenceInputFile));
@@ -391,7 +406,6 @@ public class GenomeImporter {
 
         boolean alteredChrFilenames = false;
 
-
         if (sequenceInputStream == null) {
             log.error("Invalid input for sequence creation: ");
             log.error("\tInput Stream =" + sequenceInputStream);
@@ -446,7 +460,6 @@ public class GenomeImporter {
                     chr = fastaDataLine.substring(1, whitespaceIndex).trim();
                     chrSize = 0;
                     String chrFileName = chr + GenomeManager.SEQUENCE_FILE_EXTENSION;
-
                     String legalFileName = FileUtils.legalFileName(chrFileName);
                     if (!chrFileName.equals(legalFileName)) {
                         alteredChrFilenames = true;
@@ -455,6 +468,7 @@ public class GenomeImporter {
                     File chromosomeSequenceFile = new File(genomeSequenceFolder, legalFileName);
                     chromosomeSequenceFile.createNewFile();
 
+
                     if (chromosomeFileWriter != null) {
                         chromosomeFileWriter.close();
                         chromosomeFileWriter = null;
@@ -470,10 +484,8 @@ public class GenomeImporter {
             if (chr != null) {
                 chromSizes.put(chr, chrSize);
             }
-
             return alteredChrFilenames;
 
-
         } finally {
             if (chromosomeFileWriter != null) {
                 chromosomeFileWriter.close();
@@ -484,4 +496,6 @@ public class GenomeImporter {
         }
 
     }
+
+
 }
diff --git a/src/org/broad/igv/feature/GenomeManager.java b/src/org/broad/igv/feature/GenomeManager.java
index 58c1da5..3d6aaaa 100644
--- a/src/org/broad/igv/feature/GenomeManager.java
+++ b/src/org/broad/igv/feature/GenomeManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -26,13 +26,16 @@
 package org.broad.igv.feature;
 
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
-import static org.broad.igv.IGVConstants.*;
+import org.broad.igv.Globals;
 import org.broad.igv.PreferenceManager;
+import org.broad.igv.ui.UIConstants;
+
+
+import org.broad.igv.ui.util.ConfirmDialog;
 import org.broad.igv.ui.util.MessageUtils;
 import org.broad.igv.ui.util.ProgressMonitor;
-import org.broad.igv.util.ConnectionTimerTask;
 import org.broad.igv.util.FileUtils;
+import org.broad.igv.util.IGVHttpUtils;
 import org.broad.igv.util.Utilities;
 
 import javax.swing.*;
@@ -63,14 +66,12 @@ public class GenomeManager {
 
     final public static String ALIAS_URL = "http://www.broadinstitute.org/igvdata/genomes/alias.txt";
     final public static String USER_DEFINED_GENOME_LIST_FILE = "user-defined-genomes.txt";
-    final public static String GENOME_ID_KEY = "id";
-    final public static String GENOME_NAME_KEY = "name";
-    final public static String GENOME_CYTOBAND_FILE_KEY = "cytobandFile";
-    final public static String GENOME_GENE_FILE_KEY = "geneFile";
-    final public static String GENOME_SEQUENCE_URL_KEY = "sequenceURL";
 
-    final public static String DEFAULT_GENOME_ID = "hg18";
-    private static GenomeDescriptor DEFAULT;
+    private static GenomeDescriptor DEFAULT_GENOME;
+    File GENOME_CACHE_DIRECTORY;
+    private LinkedHashSet<GenomeListItem> userDefinedGenomeArchiveList;
+    private LinkedHashSet<GenomeListItem> cachedGenomeArchiveList;
+    private LinkedHashSet<GenomeListItem> serverGenomeArchiveList;
 
 
     /**
@@ -79,6 +80,7 @@ public class GenomeManager {
     private GenomeManager() {
         genomeDescriptorMap = new HashMap();
         genomes = new Hashtable();
+        GENOME_CACHE_DIRECTORY = UIConstants.getGenomeCacheDirectory();
     }
 
     /**
@@ -101,6 +103,10 @@ public class GenomeManager {
      */
     public Genome getGenome(String id) {
 
+        if (log.isDebugEnabled()) {
+            log.debug("Enter getGenome: " + id);
+        }
+
         Genome genome = genomes.get(id);
         if (genome == null) {
 
@@ -108,42 +114,50 @@ public class GenomeManager {
             if (genomeDescriptor == null) {
                 return null;
             } else {
-                InputStream is = null;
-                try {
+                genome = loadGenome(genomeDescriptor);
+                log.info("Genome loaded");
+                genomes.put(id, genome);
 
-                    InputStream inputStream = genomeDescriptor.getCytoBandStream();
-                    if (inputStream == null) {
-                        return null;
-                    }
+            }
+        }
+        return genome;
+    }
 
-                    BufferedReader reader;
-                    if (genomeDescriptor.isCytoBandFileGZipFormat()) {
-                        is = new GZIPInputStream(inputStream);
-                        reader = new BufferedReader(new InputStreamReader(is));
-                    } else {
-                        is = new BufferedInputStream(inputStream);
-                        reader = new BufferedReader(new InputStreamReader(is));
-                    }
+    private Genome loadGenome(GenomeDescriptor genomeDescriptor) {
+        InputStream is = null;
+        try {
 
-                    genome = new Genome(id);
-                    LinkedHashMap<String, Chromosome> chromMap = CytoBandFileParser.loadData(reader);
-                    genome.setChromosomeMap(chromMap, genomeDescriptor.isChromosomesAreOrdered());
-                    genomes.put(id, genome);
+            InputStream inputStream = genomeDescriptor.getCytoBandStream();
+            if (inputStream == null) {
+                return null;
+            }
 
-                } catch (IOException ex) {
-                    log.warn("Error loading the genome!", ex);
-                } finally {
-                    try {
-                        if (is != null) {
-                            is.close();
-                        }
-                    } catch (IOException ex) {
-                        log.warn("Error closing zip stream!", ex);
-                    }
+            BufferedReader reader;
+            if (genomeDescriptor.isCytoBandFileGZipFormat()) {
+                is = new GZIPInputStream(inputStream);
+                reader = new BufferedReader(new InputStreamReader(is));
+            } else {
+                is = new BufferedInputStream(inputStream);
+                reader = new BufferedReader(new InputStreamReader(is));
+            }
+
+            Genome genome = new Genome(genomeDescriptor.getId());
+            LinkedHashMap<String, Chromosome> chromMap = CytoBandFileParser.loadData(reader);
+            genome.setChromosomeMap(chromMap, genomeDescriptor.isChromosomesAreOrdered());
+            return genome;
+
+        } catch (IOException ex) {
+            log.warn("Error loading the genome!", ex);
+            throw new RuntimeException("Error loading genome: " + genomeDescriptor.getName());
+        } finally {
+            try {
+                if (is != null) {
+                    is.close();
                 }
+            } catch (IOException ex) {
+                log.warn("Error closing zip stream!", ex);
             }
         }
-        return genome;
     }
 
 
@@ -191,8 +205,7 @@ public class GenomeManager {
 
     /**
      * Locates a genome in the set of known genome archive locations -
-     * then loads it. This method is used ONLY for command line loading
-     * of genomes.
+     * then loads it.
      *
      * @param genome The genome to load.
      */
@@ -203,11 +216,13 @@ public class GenomeManager {
         }
 
 
-        LinkedHashSet<GenomeListItem> genomes = getAllGenomeArchives(null);
+        LinkedHashSet<GenomeListItem> genomes = getAllGenomeArchives();
         for (GenomeListItem item : genomes) {
             if (item.getId().equalsIgnoreCase(genome)) {
                 String url = item.getLocation();
-                loadGenome(url, item.isUserDefined(), null);
+                if (!isGenomeLoaded(item.getId())) {
+                    loadGenome(url, item.isUserDefined(), null);
+                }
                 break;
             }
         }
@@ -231,6 +246,11 @@ public class GenomeManager {
     public void loadGenome(GenomeListItem item, boolean isUserDefined)
             throws FileNotFoundException {
 
+
+        if (log.isDebugEnabled()) {
+            log.debug("Enter loadGenome");
+        }
+
         if (isGenomeLoaded(item.getId())) {
             return;
         }
@@ -264,10 +284,10 @@ public class GenomeManager {
         GenomeListItem genomeListItem = null;
 
         if (genomeArchiveFileLocation == null) {
-            return GenomeManager.getDefaultGenomeListItem();
+            return getDefaultGenomeListItem();
         }
 
-        if (!genomeArchiveFileLocation.trim().endsWith(GENOME_FILE_EXTENSION)) {
+        if (!genomeArchiveFileLocation.trim().endsWith(Globals.GENOME_FILE_EXTENSION)) {
             throw new RuntimeException(
                     "The extension of archive [" + genomeArchiveFileLocation + "] is not an IGV genome archive extension");
         }
@@ -284,6 +304,7 @@ public class GenomeManager {
                 }
 
                 if (genomeArchiveFileLocation.toLowerCase().startsWith("http:") ||
+                        genomeArchiveFileLocation.toLowerCase().startsWith("https:") ||
                         genomeArchiveFileLocation.toLowerCase().startsWith("file:")) {
 
                     URL genomeArchiveURL = new URL(genomeArchiveFileLocation);
@@ -381,24 +402,19 @@ public class GenomeManager {
 
         try {
             if (archiveFile.exists()) {
-                // Force an update of cached genomes periodically
+                // Force an restorePersistentState of cached genomes periodically
                 boolean forceUpdate = ((System.currentTimeMillis() - archiveFile.lastModified()) / 1000) > GENOME_REFRESH_FREQ;
                 if (forceUpdate) {
                     log.info("Refreshing genome: " + genomeArchiveURL.toString());
                     File tmpFile = new File(archiveFile.getAbsolutePath() + ".tmp");
-
-                    URLConnection connection = genomeArchiveURL.openConnection();
-                    connection.setConnectTimeout(10000);
-                    is = connection.getInputStream();
+                    is = IGVHttpUtils.openConnectionStream(genomeArchiveURL);
                     FileUtils.createFileFromStream(is, tmpFile);
                     FileUtils.copyFile(tmpFile, archiveFile);
                     tmpFile.deleteOnExit();
                 }
             } else {
                 // Copy file directly from the server to local cache.
-                URLConnection connection = genomeArchiveURL.openConnection();
-                connection.setConnectTimeout(10000);
-                is = connection.getInputStream();
+                is = IGVHttpUtils.openConnectionStream(genomeArchiveURL);
                 FileUtils.createFileFromStream(is, archiveFile);
             }
         }
@@ -448,20 +464,21 @@ public class GenomeManager {
                 String zipEntryName = zipEntry.getName();
                 zipEntries.put(zipEntryName, zipEntry);
 
-                if (zipEntryName.equalsIgnoreCase(GENOME_ARCHIVE_PROPERTY_FILE_NAME)) {
+                if (zipEntryName.equalsIgnoreCase(Globals.GENOME_ARCHIVE_PROPERTY_FILE_NAME)) {
                     InputStream inputStream = zipFile.getInputStream(zipEntry);
                     Properties properties = new Properties();
                     properties.load(inputStream);
 
                     // Cytoband
-                    String cytobandZipEntryName = properties.getProperty(GENOME_ARCHIVE_CYTOBAND_FILE_KEY);
+                    String cytobandZipEntryName = properties.getProperty(Globals.GENOME_ARCHIVE_CYTOBAND_FILE_KEY);
 
                     // RefFlat
-                    String geneFileName = properties.getProperty(GENOME_ARCHIVE_GENE_FILE_KEY);
+                    String geneFileName = properties.getProperty(Globals.GENOME_ARCHIVE_GENE_FILE_KEY);
 
-                    String sequenceLocation = properties.getProperty(GENOME_ARCHIVE_SEQUENCE_FILE_LOCATION_KEY);
+                    String sequenceLocation = properties.getProperty(Globals.GENOME_ARCHIVE_SEQUENCE_FILE_LOCATION_KEY);
 
-                    if ((sequenceLocation != null) && !sequenceLocation.startsWith("http:")) {
+                    if ((sequenceLocation != null) && !(sequenceLocation.startsWith("http:") ||
+                            sequenceLocation.startsWith("https:"))) {
                         File tempZipFile = new File(zipFilePath);
                         File sequenceFolder = new File(tempZipFile.getParent(), sequenceLocation);
                         sequenceLocation = sequenceFolder.getCanonicalPath();
@@ -470,7 +487,7 @@ public class GenomeManager {
 
 
                     int version = 0;
-                    String versionString = properties.getProperty(GENOME_ARCHIVE_VERSION_KEY);
+                    String versionString = properties.getProperty(Globals.GENOME_ARCHIVE_VERSION_KEY);
                     if (versionString != null) {
                         try {
                             version = Integer.parseInt(versionString);
@@ -478,7 +495,7 @@ public class GenomeManager {
                             log.error("Error parsing version string: " + versionString);
                         }
                     }
-                    
+
                     boolean chrNamesAltered = false;
                     String chrNamesAlteredString = properties.getProperty("filenamesAltered");
                     if (chrNamesAlteredString != null) {
@@ -489,9 +506,8 @@ public class GenomeManager {
                         }
                     }
 
-
                     boolean chromosomesAreOrdered = false;
-                    String tmp = properties.getProperty(GENOME_ORDERED_KEY);
+                    String tmp = properties.getProperty(Globals.GENOME_ORDERED_KEY);
                     if (tmp != null) {
                         try {
                             chromosomesAreOrdered = Boolean.parseBoolean(tmp);
@@ -503,13 +519,13 @@ public class GenomeManager {
 
                     // The new descriptor
                     genomeDescriptor = new GenomeZipDescriptor(
-                            properties.getProperty(GENOME_ARCHIVE_NAME_KEY),
+                            properties.getProperty(Globals.GENOME_ARCHIVE_NAME_KEY),
                             version,
                             chrNamesAltered,
-                            properties.getProperty(GENOME_ARCHIVE_ID_KEY),
+                            properties.getProperty(Globals.GENOME_ARCHIVE_ID_KEY),
                             cytobandZipEntryName,
                             geneFileName,
-                            properties.getProperty(IGVConstants.GENOME_GENETRACK_NAME, "Gene"),
+                            properties.getProperty(Globals.GENOME_GENETRACK_NAME, "Gene"),
                             sequenceLocation,
                             zipFile,
                             zipEntries,
@@ -531,14 +547,6 @@ public class GenomeManager {
         return genomeDescriptor;
     }
 
-    /**
-     * Gets a map of all genome descriptor stored in IGV.
-     *
-     * @return A map of GenomeDescriptor objects keyed by genome id.
-     */
-    public Map<String, GenomeDescriptor> getGenomeDescriptorMap() {
-        return genomeDescriptorMap;
-    }
 
     /**
      * Gets the descriptor for a specific genome.
@@ -550,11 +558,13 @@ public class GenomeManager {
         if (genomeDescriptorMap.containsKey(id)) {
             return genomeDescriptorMap.get(id);
         } else {
-            return GenomeManager.getDefaultGenomeDescriptor();
+            return getDefaultGenomeDescriptor();
         }
     }
 
 
+    boolean serverGenomeListUnreachable = false;
+
     /**
      * Gets a list of all the server genome archive files that
      * IGV knows about.
@@ -568,149 +578,166 @@ public class GenomeManager {
     public LinkedHashSet<GenomeListItem> getServerGenomeArchiveList(Set excludedArchivesUrls)
             throws IOException {
 
-        LinkedHashSet<GenomeListItem> genomeItemList = new LinkedHashSet();
-        BufferedReader dataReader = null;
-        InputStream inputStream = null;
-        try {
-
-            URL serverGenomeArchiveList = new URL(PreferenceManager.getInstance().getGenomeListURL());
-            final URLConnection connection = serverGenomeArchiveList.openConnection();
+        if (serverGenomeArchiveList == null) {
+            if (serverGenomeListUnreachable) {
+                return null;
+            }
 
-            ConnectionTimerTask timerTask = new ConnectionTimerTask(connection);
-            (new java.util.Timer()).schedule(timerTask, 20000);
-            connection.setConnectTimeout(10000);
-            connection.setReadTimeout(10000);
-            inputStream = connection.getInputStream();
+            serverGenomeArchiveList = new LinkedHashSet();
+            BufferedReader dataReader = null;
+            InputStream inputStream = null;
+            HttpURLConnection conn = null;
+            try {
+                String genomeListURLString = PreferenceManager.getInstance().getGenomeListURL();
+                URL serverGenomeURL = new URL(genomeListURLString);
+
+                if (genomeListURLString.startsWith("ftp:")) {
+                    MessageUtils.showMessage("FTP protocol not supported for genome URL");
+
+                } else if (genomeListURLString.startsWith("http:") || genomeListURLString.startsWith("https:")) {
+                    conn = IGVHttpUtils.openConnection(serverGenomeURL);
+                    conn.setReadTimeout(10000);
+                    conn.setConnectTimeout(10000);
+                    inputStream = IGVHttpUtils.openConnectionStream(serverGenomeURL);
+                    inputStream = IGVHttpUtils.openHttpStream(serverGenomeURL, conn);
+                } else {
+                    File file = new File(genomeListURLString.startsWith("file:") ? serverGenomeURL.getFile() : genomeListURLString);
+                    inputStream = new FileInputStream(file);
+                }
 
-            timerTask.cancel();
 
-            dataReader = new BufferedReader(new InputStreamReader(inputStream));
+                dataReader = new BufferedReader(new InputStreamReader(inputStream));
 
-            boolean wasHeaderRead = false;
-            String genomeRecord;
-            while ((genomeRecord = dataReader.readLine()) != null) {
+                boolean wasHeaderRead = false;
+                String genomeRecord;
+                while ((genomeRecord = dataReader.readLine()) != null) {
 
-                // Check for valid data header
-                if (!wasHeaderRead) {
-                    wasHeaderRead = true;
-                    if ((genomeRecord == null) || !genomeRecord.trim().equalsIgnoreCase(SERVER_GENOME_LIST_HEADER)) {
-                        String message = "The genome list returned " + "from the server had an invalid header record!";
-                        log.error(message);
-                        throw new GenomeException(message);
+                    if (genomeRecord.startsWith("<") || genomeRecord.startsWith("(#")) {
+                        continue;
                     }
-                    continue;
-                }
 
-                if (genomeRecord != null) {
-                    genomeRecord = genomeRecord.trim();
+                    if (genomeRecord != null) {
+                        genomeRecord = genomeRecord.trim();
 
-                    String[] fields = genomeRecord.split("\t");
+                        String[] fields = genomeRecord.split("\t");
 
-                    if ((fields != null) && (fields.length >= 3)) {
+                        if ((fields != null) && (fields.length >= 3)) {
 
-                        // Throw away records we don't want to see
-                        if (excludedArchivesUrls != null) {
-                            if (excludedArchivesUrls.contains(fields[1])) {
-                                continue;
+                            // Throw away records we don't want to see
+                            if (excludedArchivesUrls != null) {
+                                if (excludedArchivesUrls.contains(fields[1])) {
+                                    continue;
+                                }
                             }
-                        }
-                        int version = 0;
-                        if (fields.length > 3) {
-                            try {
-                                version = Integer.parseInt(fields[3]);
-                            } catch (Exception e) {
-                                log.error("Error parsing genome version: " + fields[0], e);
+                            int version = 0;
+                            if (fields.length > 3) {
+                                try {
+                                    version = Integer.parseInt(fields[3]);
+                                } catch (Exception e) {
+                                    log.error("Error parsing genome version: " + fields[0], e);
+                                }
                             }
-                        }
 
-                        try {
-                            GenomeListItem item = new GenomeListItem(fields[0], fields[1], fields[2], version, false);
-                            genomeItemList.add(item);
-                        } catch (Exception e) {
-                            log.error(
-                                    "Error reading a line from server genome list" + " line was: [" + genomeRecord + "]",
-                                    e);
+                            //String displayableName, String url, String id, int version, boolean isUserDefined
+                            String name = fields[0];
+                            String url = fields[1];
+                            String id = fields[2];
+
+                            boolean valid = true;
+                            if (url.length() == 0) {
+                                log.error("Genome entry : " + name + " has an empty URL string.  Check for extra tabs in the definition file: " +
+                                        PreferenceManager.getInstance().getGenomeListURL());
+                                valid = false;
+                            }
+                            // TODO -- more validation
+
+
+                            if (valid) {
+                                try {
+                                    GenomeListItem item = new GenomeListItem(fields[0], fields[1], fields[2], version, false);
+                                    serverGenomeArchiveList.add(item);
+                                } catch (Exception e) {
+                                    log.error(
+                                            "Error reading a line from server genome list" + " line was: [" + genomeRecord + "]",
+                                            e);
+                                }
+                            }
+                        } else {
+                            log.error("Found invalid server genome list record: " + genomeRecord);
                         }
-                    } else {
-                        log.error("Found invalid server genome list record: " + genomeRecord);
                     }
                 }
-            }
-        } catch (Exception e) {
-            log.error("Error fetching genome list: ", e);
-            MessageUtils.showMessage(IGVConstants.CANNOT_ACCESS_SERVER_GENOME_LIST);
-        } finally {
-            if (dataReader != null) {
-                dataReader.close();
-            }
-            if (inputStream != null) {
-                inputStream.close();
+            } catch (Exception e) {
+                serverGenomeListUnreachable = true;
+                log.error("Error fetching genome list: ", e);
+                ConfirmDialog.optionallyShowInfoDialog("Warning: There was an error connecting to the genome server (" +
+                        e.toString() + ").    Only locally defined genomes will be available.",
+                        PreferenceManager.SHOW_GENOME_SERVER_WARNING);
+            } finally {
+                if (dataReader != null) {
+                    dataReader.close();
+                }
+                if (inputStream != null) {
+                    inputStream.close();
+                }
             }
         }
-        return genomeItemList;
+        return serverGenomeArchiveList;
     }
 
     /**
      * Gets a list of all the user-defined genome archive files that
      * IGV knows about.
      *
-     * @param excludedArchivesUrls The set of file location to exclude in the
-     *                             return list.
      * @return LinkedHashSet<GenomeListItem>
      * @throws IOException
      * @see GenomeListItem
      */
-    public LinkedHashSet<GenomeListItem> getUserDefinedGenomeArchiveList(Set excludedArchivesUrls)
+    public LinkedHashSet<GenomeListItem> getUserDefinedGenomeArchiveList()
             throws IOException {
 
-        boolean clientGenomeListNeedsRebuilding = false;
-        LinkedHashSet<GenomeListItem> genomeItemList = new LinkedHashSet();
+        if (userDefinedGenomeArchiveList == null) {
 
-        File listFile = new File(GENOME_CACHE_DIRECTORY, USER_DEFINED_GENOME_LIST_FILE);
+            userDefinedGenomeArchiveList = new LinkedHashSet();
 
-        Properties listProperties = retrieveUserDefinedGenomeListFromFile(listFile);
+            File listFile = new File(GENOME_CACHE_DIRECTORY, USER_DEFINED_GENOME_LIST_FILE);
+            boolean clientGenomeListNeedsRebuilding = false;
+            Properties listProperties = retrieveUserDefinedGenomeListFromFile(listFile);
 
-        if (listProperties != null) {
+            if (listProperties != null) {
 
-            Collection records = listProperties.values();
+                Collection records = listProperties.values();
 
-            for (Object value : records) {
+                for (Object value : records) {
 
-                String record = (String) value;
-                if (record.trim().equals("")) {
-                    continue;
-                }
+                    String record = (String) value;
+                    if (record.trim().equals("")) {
+                        continue;
+                    }
 
-                String[] fields = record.split("\t");
+                    String[] fields = record.split("\t");
 
-                // Throw away records we don't want to see
-                if (excludedArchivesUrls != null) {
-                    if (excludedArchivesUrls.contains(fields[1])) {
+                    File file = new File(fields[1]);
+                    if (file.isDirectory()) {
+                        continue;
+                    }
+                    if (!file.exists()) {
+                        clientGenomeListNeedsRebuilding = true;
                         continue;
                     }
-                }
-
-                File file = new File(fields[1]);
-                if (file.isDirectory()) {
-                    continue;
-                }
-                if (!file.exists()) {
-                    clientGenomeListNeedsRebuilding = true;
-                    continue;
-                }
 
-                if (!file.getName().toLowerCase().endsWith(GENOME_FILE_EXTENSION)) {
-                    continue;
+                    if (!file.getName().toLowerCase().endsWith(Globals.GENOME_FILE_EXTENSION)) {
+                        continue;
+                    }
+                    GenomeListItem item = new GenomeListItem(fields[0], file.getAbsolutePath(), fields[2], 0, true);
+                    userDefinedGenomeArchiveList.add(item);
                 }
-                GenomeListItem item = new GenomeListItem(fields[0], file.getAbsolutePath(),
-                        fields[2], 0, true);
-                genomeItemList.add(item);
+            }
+            if (clientGenomeListNeedsRebuilding) {
+                rebuildClientGenomeList(userDefinedGenomeArchiveList);
             }
         }
-        if (clientGenomeListNeedsRebuilding) {
-            rebuildClientGenomeList(genomeItemList);
-        }
-        return genomeItemList;
+        return userDefinedGenomeArchiveList;
     }
 
     /**
@@ -720,7 +747,7 @@ public class GenomeManager {
 
         File[] files = GENOME_CACHE_DIRECTORY.listFiles();
         for (File file : files) {
-            if (file.getName().toLowerCase().endsWith(GENOME_FILE_EXTENSION)) {
+            if (file.getName().toLowerCase().endsWith(Globals.GENOME_FILE_EXTENSION)) {
                 file.delete();
             }
         }
@@ -731,133 +758,119 @@ public class GenomeManager {
      * Gets a list of all the locally cached genome archive files that
      * IGV knows about.
      *
-     * @param excludedArchivesUrls The set of file location to exclude in the
-     *                             return list.
      * @return LinkedHashSet<GenomeListItem>
      * @throws IOException
      * @see GenomeListItem
      */
-    public LinkedHashSet<GenomeListItem> getCachedGenomeArchiveList(Set excludedArchivesUrls)
+    public LinkedHashSet<GenomeListItem> getCachedGenomeArchiveList()
             throws IOException {
 
-        LinkedHashSet<GenomeListItem> genomeItemList = new LinkedHashSet();
-
-        if (!GENOME_CACHE_DIRECTORY.exists()) {
-            return genomeItemList;
-        }
-
-        File[] files = GENOME_CACHE_DIRECTORY.listFiles();
-        for (File file : files) {
 
-            if (file.isDirectory()) {
-                continue;
-            }
+        if (cachedGenomeArchiveList == null) {
+            cachedGenomeArchiveList = new LinkedHashSet();
 
-            if (!file.getName().toLowerCase().endsWith(GENOME_FILE_EXTENSION)) {
-                continue;
+            if (!GENOME_CACHE_DIRECTORY.exists()) {
+                return cachedGenomeArchiveList;
             }
 
-            URL zipUrl = file.toURI().toURL();
+            File[] files = GENOME_CACHE_DIRECTORY.listFiles();
+            for (File file : files) {
 
-            // Throw away records we don't want to see
-            if (excludedArchivesUrls != null) {
-                if (excludedArchivesUrls.contains(URLDecoder.decode(zipUrl.getFile(), "UTF-8"))) {
+                if (file.isDirectory()) {
                     continue;
                 }
-            }
 
-            ZipFile zipFile = null;
-            FileInputStream fis = null;
-            ZipInputStream zipInputStream = null;
-            try {
+                if (!file.getName().toLowerCase().endsWith(Globals.GENOME_FILE_EXTENSION)) {
+                    continue;
+                }
 
-                zipFile = new ZipFile(file);
-                fis = new FileInputStream(file);
-                zipInputStream = new ZipInputStream(new BufferedInputStream(fis));
+                URL zipUrl = file.toURI().toURL();
 
-                ZipEntry zipEntry = zipFile.getEntry(GENOME_ARCHIVE_PROPERTY_FILE_NAME);
-                if (zipEntry == null) {
-                    continue;    // Should never happen
-                }
+                ZipFile zipFile = null;
+                FileInputStream fis = null;
+                ZipInputStream zipInputStream = null;
+                try {
 
-                InputStream inputStream = zipFile.getInputStream(zipEntry);
-                Properties properties = new Properties();
-                properties.load(inputStream);
+                    zipFile = new ZipFile(file);
+                    fis = new FileInputStream(file);
+                    zipInputStream = new ZipInputStream(new BufferedInputStream(fis));
 
-                int version = 0;
-                if (properties.containsKey(GENOME_ARCHIVE_VERSION_KEY)) {
-                    try {
-                        version = Integer.parseInt(
-                                properties.getProperty(GENOME_ARCHIVE_VERSION_KEY));
-                    } catch (Exception e) {
-                        log.error("Error parsing genome version: " + version, e);
+                    ZipEntry zipEntry = zipFile.getEntry(Globals.GENOME_ARCHIVE_PROPERTY_FILE_NAME);
+                    if (zipEntry == null) {
+                        continue;    // Should never happen
                     }
-                }
 
-                GenomeListItem item =
-                        new GenomeListItem(properties.getProperty(GENOME_ARCHIVE_NAME_KEY),
-                                file.getAbsolutePath(),
-                                properties.getProperty(GENOME_ARCHIVE_ID_KEY),
-                                version,
-                                false);
-                genomeItemList.add(item);
-            } catch (ZipException ex) {
-                log.error("\nZip error unzipping cached genome.", ex);
-                JOptionPane.showMessageDialog(null, "Fatal error loading genome file: " + file.getAbsolutePath() +
-                        "\n     *** " + ex.getMessage() + " ***" +
-                        "\nIGV must exit.  If this problem persists contact igv-help at broadinstitute.org");
-                try {
-                    file.deleteOnExit();
-                    zipInputStream.close();
-                }
-                catch (Exception e) {
-                    //ignore exception when trying to delete file
-                }
-                System.exit(-1);
+                    InputStream inputStream = zipFile.getInputStream(zipEntry);
+                    Properties properties = new Properties();
+                    properties.load(inputStream);
 
+                    int version = 0;
+                    if (properties.containsKey(Globals.GENOME_ARCHIVE_VERSION_KEY)) {
+                        try {
+                            version = Integer.parseInt(
+                                    properties.getProperty(Globals.GENOME_ARCHIVE_VERSION_KEY));
+                        } catch (Exception e) {
+                            log.error("Error parsing genome version: " + version, e);
+                        }
+                    }
 
-            } catch (IOException ex) {
-                log.warn("\nIO error unzipping cached genome.", ex);
-                try {
-                    file.delete();
-                }
-                catch (Exception e) {
-                    //ignore exception when trying to delete file
-                }
-            } finally {
-                try {
-                    if (zipInputStream != null) {
+                    GenomeListItem item =
+                            new GenomeListItem(properties.getProperty(Globals.GENOME_ARCHIVE_NAME_KEY),
+                                    file.getAbsolutePath(),
+                                    properties.getProperty(Globals.GENOME_ARCHIVE_ID_KEY),
+                                    version,
+                                    false);
+                    cachedGenomeArchiveList.add(item);
+                } catch (ZipException ex) {
+                    log.error("\nZip error unzipping cached genome.", ex);
+                    try {
+                        file.delete();
                         zipInputStream.close();
                     }
-                    if (zipFile != null) {
-                        zipFile.close();
-                    }
-                    if (fis != null) {
-                        fis.close();
+                    catch (Exception e) {
+                        //ignore exception when trying to delete file
                     }
                 } catch (IOException ex) {
-                    log.warn("Error closing genome zip stream!", ex);
+                    log.warn("\nIO error unzipping cached genome.", ex);
+                    try {
+                        file.delete();
+                    }
+                    catch (Exception e) {
+                        //ignore exception when trying to delete file
+                    }
+                } finally {
+                    try {
+                        if (zipInputStream != null) {
+                            zipInputStream.close();
+                        }
+                        if (zipFile != null) {
+                            zipFile.close();
+                        }
+                        if (fis != null) {
+                            fis.close();
+                        }
+                    } catch (IOException ex) {
+                        log.warn("Error closing genome zip stream!", ex);
+                    }
                 }
             }
         }
 
-        return genomeItemList;
+        return cachedGenomeArchiveList;
     }
 
     /**
      * Gets a list of all the server and client-side genome archive files that
      * IGV knows about.
      *
-     * @param excludedArchivesUrls The set of file location to exclude in the
-     *                             return list.
      * @return LinkedHashSet<GenomeListItem>
      * @throws IOException
      * @see GenomeListItem
      */
-    public LinkedHashSet<GenomeListItem> getAllGenomeArchives(Set excludedArchivesUrls)
+    public LinkedHashSet<GenomeListItem> getAllGenomeArchives()
             throws IOException {
 
-        LinkedHashSet<GenomeListItem> genomes = new LinkedHashSet();
+        LinkedHashSet<GenomeListItem> genomeListItems = new LinkedHashSet();
 
         // Build a single available genome list from both client, server
         // and cached information. This allows us to process
@@ -866,32 +879,35 @@ public class GenomeManager {
         try {
             serverSideItemList = getServerGenomeArchiveList(null);
         } catch (UnknownHostException e) {
-            log.error(IGVConstants.CANNOT_ACCESS_SERVER_GENOME_LIST, e);
+            log.error(UIConstants.CANNOT_ACCESS_SERVER_GENOME_LIST, e);
         } catch (SocketException e) {
-            log.error(IGVConstants.CANNOT_ACCESS_SERVER_GENOME_LIST, e);
+            log.error(UIConstants.CANNOT_ACCESS_SERVER_GENOME_LIST, e);
         }
 
-        LinkedHashSet<GenomeListItem> cacheGenomeItemList = getCachedGenomeArchiveList(excludedArchivesUrls);
+        LinkedHashSet<GenomeListItem> cacheGenomeItemList = getCachedGenomeArchiveList();
 
-        LinkedHashSet<GenomeListItem> userDefinedItemList = getUserDefinedGenomeArchiveList(excludedArchivesUrls);
+        LinkedHashSet<GenomeListItem> userDefinedItemList = getUserDefinedGenomeArchiveList();
 
 
         if (serverSideItemList != null) {
-            genomes.addAll(serverSideItemList);
+            genomeListItems.addAll(serverSideItemList);
         }
         if (userDefinedItemList != null) {
-            genomes.addAll(userDefinedItemList);
+            genomeListItems.addAll(userDefinedItemList);
         }
         if (cacheGenomeItemList != null) {
-            genomes.addAll(cacheGenomeItemList);
+            genomeListItems.addAll(cacheGenomeItemList);
         }
 
-        if (genomes.isEmpty()) {
-            GenomeListItem defaultItem = GenomeManager.getDefaultGenomeListItem();
-            genomes.add(defaultItem);
+        if (genomeListItems.isEmpty()) {
+            GenomeDescriptor defaultDes = getDefaultGenomeDescriptor();
+            GenomeListItem defaultItem = new GenomeListItem(defaultDes.getName(), null, defaultDes.getId(), 0, false);
+            genomeListItems.add(defaultItem);
+            Genome genome = loadGenome(defaultDes);
+            genomes.put(defaultDes.getId(), genome);
         }
 
-        return genomes;
+        return genomeListItems;
     }
 
     /**
@@ -1073,6 +1089,11 @@ public class GenomeManager {
             monitor.fireProgressChange(75);
         }
 
+
+        if (log.isDebugEnabled()) {
+            log.debug("Call loadGenome");
+        }
+
         return loadGenome(archiveFile.getAbsolutePath(), true, null);
     }
 
@@ -1103,19 +1124,33 @@ public class GenomeManager {
         return text;
     }
 
-    public static synchronized GenomeDescriptor getDefaultGenomeDescriptor() {
-        if (DEFAULT == null) {
-            DEFAULT = new GenomeResourceDescriptor("Human hg18", 0, "hg18",
-                    "/resources/hg18_cytoBand.txt", null, null, "http://www.broadinstitute.org/igv/sequence/hg18", false);
+
+    public GenomeListItem getTopGenomeListItem() {
+        try {
+            LinkedHashSet<GenomeListItem> allItems = getAllGenomeArchives();
+            if(allItems.size() > 0) {
+                return allItems.iterator().next();
+            }
+        } catch (IOException e) {
+            log.error("Error loading genome list: ", e);
         }
-        return DEFAULT;
+        return getDefaultGenomeListItem();
     }
 
-    public static synchronized GenomeListItem getDefaultGenomeListItem() {
+    public GenomeListItem getDefaultGenomeListItem() {
         GenomeDescriptor desc = getDefaultGenomeDescriptor();
         return new GenomeListItem(desc.getName(), null, desc.getId(), 0, false);
     }
 
+
+    private GenomeDescriptor getDefaultGenomeDescriptor() {
+        if (DEFAULT_GENOME == null) {
+            DEFAULT_GENOME = new GenomeResourceDescriptor("Human hg18", 0, "hg18",
+                    "/resources/hg18_cytoBand.txt", null, null, "http://www.broadinstitute.org/igv/sequence/hg18", false);
+        }
+        return DEFAULT_GENOME;
+    }
+
     /**
      * A container for specific genome information which can be used to
      * manage loaded genomes.
@@ -1201,21 +1236,17 @@ public class GenomeManager {
 
             return getId().equals(item.getId());
         }
+
     }
 
 
-    public synchronized Map<String, String> getAliasTable() {
+    public synchronized Map<String, String> getGenomeAliasTable() {
 
         if (aliasTable == null) {
             aliasTable = new HashMap();
             try {
                 URL url = new URL(ALIAS_URL);
-
-                URLConnection conn = url.openConnection();
-                conn.setConnectTimeout(2000);
-                conn.setReadTimeout(2000);
-
-                InputStream is = conn.getInputStream();
+                InputStream is = IGVHttpUtils.openConnectionStream(url);
                 BufferedReader br = new BufferedReader(new InputStreamReader(is));
                 String nextLine = "";
                 while ((nextLine = br.readLine()) != null) {
@@ -1235,5 +1266,50 @@ public class GenomeManager {
         return aliasTable;
     }
 
+    public Map<String, String> getChromosomeAliasTable(String genomeId) {
+
+        File aliasFile = new File(GENOME_CACHE_DIRECTORY, genomeId + "_alias.tab");
+        log.debug("Looking for chromosome alias file: " + aliasFile.getAbsolutePath());
+
+        if (aliasFile.exists()) {
+            log.debug("Loading chromosome alias table ");
+
+            BufferedReader br = null;
+            Map<String, String> chrAliasTable = new HashMap();
+            try {
+                br = new BufferedReader(new FileReader(aliasFile));
+                String nextLine = "";
+                while ((nextLine = br.readLine()) != null) {
+                    String[] kv = nextLine.split("\t");
+                    if (kv.length > 1) {
+                        log.debug(kv[0] + " -> " + kv[1]);
+                        chrAliasTable.put(kv[0], kv[1]);
+                    }
+                }
+                return chrAliasTable;
+            } catch (IOException e) {
+                log.error("Error loading chr alias table", e);
+                MessageUtils.showMessage("<html>Error loading chromosome alias table.  Aliases will not be avaliable<br>" +
+                        e.toString());
+                return null;
+            }
+
+            finally {
+                if (br != null) {
+                    try {
+                        br.close();
+                    } catch (IOException e) {
+                        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+                    }
+                }
+            }
+
+
+        } else {
+            return null;
+        }
+
+    }
+
 
 }
diff --git a/src/org/broad/igv/feature/GenomeResourceDescriptor.java b/src/org/broad/igv/feature/GenomeResourceDescriptor.java
index 3f2bc7c..020a29e 100644
--- a/src/org/broad/igv/feature/GenomeResourceDescriptor.java
+++ b/src/org/broad/igv/feature/GenomeResourceDescriptor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/feature/GenomeServerException.java b/src/org/broad/igv/feature/GenomeServerException.java
index 70d6664..dc3380a 100644
--- a/src/org/broad/igv/feature/GenomeServerException.java
+++ b/src/org/broad/igv/feature/GenomeServerException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/feature/GenomeZipDescriptor.java b/src/org/broad/igv/feature/GenomeZipDescriptor.java
index 043115b..6caa47e 100644
--- a/src/org/broad/igv/feature/GenomeZipDescriptor.java
+++ b/src/org/broad/igv/feature/GenomeZipDescriptor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/feature/GisticFileParser.java b/src/org/broad/igv/feature/GisticFileParser.java
index a340063..e0c2842 100644
--- a/src/org/broad/igv/feature/GisticFileParser.java
+++ b/src/org/broad/igv/feature/GisticFileParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -29,11 +29,11 @@ package org.broad.igv.feature;
 
 import org.apache.log4j.Logger;
 import org.broad.igv.exceptions.ParserException;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.track.GisticTrack;
-import org.broad.igv.ui.IGVModel;
 import org.broad.igv.util.AsciiLineReader;
-import org.broad.igv.util.ResourceLocator;
 import org.broad.igv.util.ParsingUtils;
+import org.broad.igv.util.ResourceLocator;
 
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
@@ -59,7 +59,7 @@ public class GisticFileParser {
 
         String nextLine = null;
         try {
-            Genome genome = IGVModel.getInstance().getViewContext().getGenome();
+            Genome genome = ViewContext.getInstance().getGenome();
 
             reader = ParsingUtils.openAsciiReader(locator);
 
@@ -68,7 +68,8 @@ public class GisticFileParser {
 
             // Parse data
             // parameters to track chromosome breaks. Used in wholeGenome case.
-            GisticTrack track = new GisticTrack(locator, locator.getDisplayName());
+            GisticTrack track = new GisticTrack(locator);
+            track.setName(locator.getTrackName());
             List<GisticScore> scores = new ArrayList();
 
             nextLine = reader.readLine();
@@ -121,7 +122,7 @@ public class GisticFileParser {
                 throw new RuntimeException("No gistic scores were found.");
             } else {
                 track.setScores(scores);
-                computeWholeGenome(IGVModel.getInstance().getViewContext().getGenomeId(), track);
+                computeWholeGenome(ViewContext.getInstance().getGenomeId(), track);
                 return track;
             }
 
diff --git a/src/org/broad/igv/feature/GisticScore.java b/src/org/broad/igv/feature/GisticScore.java
index 121a332..2e664d1 100644
--- a/src/org/broad/igv/feature/GisticScore.java
+++ b/src/org/broad/igv/feature/GisticScore.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -157,6 +157,10 @@ public class GisticScore implements LocusScore {
         this.gisticType = type;
     }
 
+    public String getChr() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
     /**
      * Method description
      *
diff --git a/src/org/broad/igv/feature/Locus.java b/src/org/broad/igv/feature/Locus.java
index 0e63d54..4eece26 100644
--- a/src/org/broad/igv/feature/Locus.java
+++ b/src/org/broad/igv/feature/Locus.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,16 +22,14 @@
  */
 package org.broad.igv.feature;
 
-import org.broad.igv.util.ParsingUtils;
-
 /**
  * @author jrobinso
  */
 public class Locus {
 
     private String chr = null;
-    private int start = -1;
-    private int end = -1;
+    protected int start = -1;
+    protected int end = -1;
 
     public Locus(String chr, int start, int end) {
         this.chr = chr;
@@ -86,4 +84,20 @@ public class Locus {
     public int getEnd() {
         return end;
     }
+
+   /**
+     * Weak version of contains, does not check genome
+     *
+     * @param chr
+     * @param start
+     * @param end
+     * @return
+     */
+    public boolean contains(String chr, int start, int end) {
+        return this.chr.equals(chr) && this.start <= start && this.end >= end;
+    }
+
+    protected boolean overlaps(String chr, int start, int end) {
+        return this.chr.equals(chr) && this.start <= end && this.end >= start;
+    }
 }
diff --git a/src/org/broad/igv/feature/LocusScore.java b/src/org/broad/igv/feature/LocusScore.java
index cb9c504..a508875 100644
--- a/src/org/broad/igv/feature/LocusScore.java
+++ b/src/org/broad/igv/feature/LocusScore.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -27,13 +27,13 @@ import org.broad.igv.track.WindowFunction;
 /**
  * @author jrobinso
  */
-public interface LocusScore {
+public interface LocusScore  {
 
     public int getStart();
 
-    public void setStart(int start);
-
     public int getEnd();
+    
+    public void setStart(int start);
 
     public void setEnd(int end);
 
diff --git a/src/org/broad/igv/feature/MaximumContigGenomeException.java b/src/org/broad/igv/feature/MaximumContigGenomeException.java
index 92a1051..7862f53 100644
--- a/src/org/broad/igv/feature/MaximumContigGenomeException.java
+++ b/src/org/broad/igv/feature/MaximumContigGenomeException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/feature/Mutation.java b/src/org/broad/igv/feature/Mutation.java
index 64c4987..d748d06 100644
--- a/src/org/broad/igv/feature/Mutation.java
+++ b/src/org/broad/igv/feature/Mutation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -21,11 +21,10 @@ package org.broad.igv.feature;
 
 import org.apache.log4j.Logger;
 import org.broad.igv.PreferenceManager;
-import org.broad.igv.util.ParsingUtils;
+import org.broad.igv.ui.util.ColorTable;
 import org.broad.igv.track.WindowFunction;
 
 import java.awt.*;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -38,117 +37,16 @@ import java.util.Map;
 public class Mutation implements Feature {
 
     private static Logger log = Logger.getLogger(Mutation.class);
+    private static Map<String, Color> colors;
 
-    private String runId;
-    private String chromosome;
+    private String sampleId;
+    private String chr;
     private int start;
     private int end;
-    private Type type;
+    private String type;
     private Color color;
-    private String newBase;
-    private String mClass;
-    private static Map<Type, Color> colors;
+    private Map<String, String> attributes;
 
-    public void setChromosome(String chr) {
-        this.chromosome = chromosome;
-    }
-
-    public void setName(String name) {
-        try {
-            Type newType = Type.valueOf(name);
-            type = newType;
-        }
-        catch (Exception e) {
-            log.error("Error setting mutation type", e);
-        }
-    }
-
-    public enum Type {
-
-        Indel, Missense, Nonsense, Splice_site, Synonymous, Targeted_Region, Unknown
-    }
-
-    private static Map<Mutation.Type, Color> theColorScheme;
-    static Mutation.Type[] types = new Mutation.Type[]{
-            Mutation.Type.Indel, Mutation.Type.Missense, Mutation.Type.Nonsense,
-            Mutation.Type.Splice_site, Mutation.Type.Synonymous, Mutation.Type.Targeted_Region,
-            Mutation.Type.Unknown
-    };
-
-    // TODO It would be nice is we make the ColorSchemeFactory produce
-    // ColorScheme objects instead of ContinuousColorScheme objects. This way
-    // sets of Color specific behavior could be retrieved from a single place
-    // with common behavior. For now I need to use a Map since this object was
-    // not using a ColorScheme at all. In the future we should replace
-    // Map<Mutation.Type,Color> by ColorScheme.
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public static Map<Mutation.Type, Color> getColorScheme() {
-
-        if (theColorScheme != null) {
-            return theColorScheme;
-        }
-
-        theColorScheme = PreferenceManager.getInstance().getMutationColorScheme();
-
-        // If Color Scheme info was not found in user's preferences
-        // so the scheme is still null we use the default
-        if (theColorScheme == null) {
-            theColorScheme = getDefaultColorScheme();
-        }
-
-        return theColorScheme;
-    }
-
-    // TODO same changes as getColorScheme
-    /**
-     * Method description
-     *
-     * @param colorScheme
-     */
-    public static void setColorScheme(Map<Mutation.Type, Color> colorScheme) {
-        theColorScheme = colorScheme;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public static Map<Mutation.Type, Color> getDefaultColorScheme() {
-
-        HashMap<Mutation.Type, Color> scheme = new HashMap<Mutation.Type, Color>();
-        for (Mutation.Type mutType : types) {
-            scheme.put(mutType, Mutation.getColor(mutType));
-        }
-
-        return scheme;
-    }
-
-    /**
-     * Method description
-     */
-    public static void reloadPreferences() {
-        theColorScheme = PreferenceManager.getInstance().getMutationColorScheme();
-    }
-
-    private static Color getColor(Type type) {
-
-        if (colors == null) {
-            colors = new HashMap();
-            colors.put(Type.Indel, Color.GREEN);
-            colors.put(Type.Missense, new Color(161, 45, 238));
-            colors.put(Type.Nonsense, new Color(103, 61, 123));
-            colors.put(Type.Splice_site, Color.MAGENTA);
-            colors.put(Type.Synonymous, Color.CYAN);
-            colors.put(Type.Targeted_Region, Color.ORANGE);
-        }
-
-        return colors.containsKey(type) ? colors.get(type) : Color.black;
-    }
 
     /**
      * Constructs ...
@@ -159,9 +57,9 @@ public class Mutation implements Feature {
      * @param end
      * @param type
      */
-    public Mutation(String runId, String chromosome, int start, int end, Type type) {
-        this.runId = runId;
-        this.chromosome = chromosome;
+    public Mutation(String runId, String chromosome, int start, int end, String type) {
+        this.sampleId = runId;
+        this.chr = chromosome;
         this.start = start;
         this.end = end;
         this.type = type;
@@ -173,16 +71,24 @@ public class Mutation implements Feature {
      * @param mutation
      */
     public Mutation(Mutation mutation) {
-        this.runId = mutation.runId;
-        this.chromosome = mutation.chromosome;
+        this.sampleId = mutation.sampleId;
+        this.chr = mutation.chr;
         this.start = mutation.start;
         this.end = mutation.end;
         this.type = mutation.type;
         this.color = mutation.color;
-        this.newBase = mutation.newBase;
-        this.mClass = mutation.mClass;
     }
 
+
+    public void setChr(String chr) {
+        this.chr = chr;
+    }
+
+    public void setName(String name) {
+        type = name;
+    }
+
+
     /**
      * Method description
      *
@@ -197,8 +103,8 @@ public class Mutation implements Feature {
      *
      * @return
      */
-    public String getRunId() {
-        return runId;
+    public String getSampleId() {
+        return sampleId;
     }
 
     /**
@@ -215,7 +121,7 @@ public class Mutation implements Feature {
      *
      * @return
      */
-    public Type getMutationType() {
+    public String getMutationType() {
         return type;
     }
 
@@ -237,199 +143,88 @@ public class Mutation implements Feature {
         return getName();
     }
 
-    /**
-     * Method description
-     *
-     * @param ignored
-     * @return
-     */
+    private String valueString;
+
     public String getValueString(double position, WindowFunction ignored) {
-        if ((mClass != null) && (newBase != null)) {
-            return getName() + "<br>" + "Class     = " + mClass + "<br>New Base = " + newBase;
-        } else {
-            return getName();
+
+        if (valueString == null && attributes != null) {
+            StringBuffer buf = new StringBuffer();
+            buf.append("<html>Type: ");
+            buf.append(type +"<br>");
+            for (Map.Entry<String, String> entry : attributes.entrySet()) {
+                buf.append(entry.getKey() + ": " + entry.getValue() + "<br>");
+            }
+            valueString = buf.toString();
         }
+        return valueString;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public boolean hasScore() {
         return false;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public Strand getStrand() {
         return Strand.NONE;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public boolean hasStrand() {
         return false;
     }
 
-    /**
-     * Method description
-     *
-     * @param track
-     * @return
-     */
     public boolean overlaps(Feature track) {
         return false;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public String getChromosome() {
-        return chromosome;
+    public String getChr() {
+        return chr;
     }
 
-    /**
-     * Method description
-     *
-     * @param color
-     */
     public void setColor(Color color) {
 
         // Ignore
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public Color getColor() {
-        Color c = getColorScheme().get(getMutationType());
+        ColorTable colorTable = PreferenceManager.getInstance().getMutationColorScheme() ;
+        Color c = colorTable.get(getMutationType());
         return c;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public int getStart() {
         return start;
     }
 
-    /**
-     * Method description
-     *
-     * @param start
-     */
     public void setStart(int start) {
         this.start = start;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public int getEnd() {
         return end;
     }
 
-    /**
-     * Method description
-     *
-     * @param end
-     */
     public void setEnd(int end) {
         this.end = end;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public int[] getStarts() {
         return null;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public int[] getEnds() {
         return null;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public float getScore() {
         return 0;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public float getConfidence() {
         return 1;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public String getNewBase() {
-        return newBase;
-    }
-
-    /**
-     * Method description
-     *
-     * @param newBase
-     */
-    public void setNewBase(String newBase) {
-        this.newBase = newBase;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public String getMClass() {
-        return mClass;
-    }
-
-    /**
-     * Method description
-     *
-     * @param mClass
-     */
-    public void setMClass(String mClass) {
-        this.mClass = mClass;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
     public int getLevel() {
         throw new UnsupportedOperationException("Not supported yet.");
     }
@@ -443,7 +238,7 @@ public class Mutation implements Feature {
      */
     public boolean contains(Feature feature) {
 
-        if (feature == null || !this.getChromosome().equals(feature.getChromosome())) {
+        if (feature == null || !this.getChr().equals(feature.getChr())) {
             return false;
         }
         if ((feature.getStart() >= this.getStart()) && (feature.getEnd() <= this.getEnd())) {
@@ -457,11 +252,10 @@ public class Mutation implements Feature {
         return location >= start && location <= end;
     }
 
-    /**
-     * Method description
-     *
-     * @param confidence
-     */
+    public String getURL() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
     public void setConfidence(float confidence) {
 
         // ignore
@@ -476,48 +270,22 @@ public class Mutation implements Feature {
         return null;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public String getIdentifier() {
         throw new UnsupportedOperationException("Not supported yet.");
     }
 
-    /**
-     * Method description
-     *
-     * @param exonIndex
-     * @return
-     */
     public AminoAcidSequence getAminoAcidSequence(int exonIndex) {
         throw new UnsupportedOperationException("Not supported yet.");
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public int getCdEnd() {
         throw new UnsupportedOperationException("Not supported yet.");
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public int getCdStart() {
         throw new UnsupportedOperationException("Not supported yet.");
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public int getLength() {
         throw new UnsupportedOperationException("Not supported yet.");
     }
@@ -525,4 +293,8 @@ public class Mutation implements Feature {
     public Map<String, String> getAttributes() {
         return null;
     }
+
+    public void setAttributes(Map<String, String> attributes) {
+        this.attributes = attributes;
+    }
 }
diff --git a/src/org/broad/igv/feature/MutationParser.java b/src/org/broad/igv/feature/MutationParser.java
index 3c5ba80..66c6426 100644
--- a/src/org/broad/igv/feature/MutationParser.java
+++ b/src/org/broad/igv/feature/MutationParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -18,15 +18,21 @@
 package org.broad.igv.feature;
 
 import org.apache.log4j.Logger;
-import org.broad.igv.exceptions.ParserException;
+import org.broad.igv.Globals;
+import org.broad.igv.exceptions.DataLoadException;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.track.FeatureCollectionSource;
 import org.broad.igv.track.FeatureTrack;
 import org.broad.igv.track.Track;
-import org.broad.igv.ui.IGVModel;
 import org.broad.igv.util.AsciiLineReader;
-import org.broad.igv.util.ResourceLocator;
 import org.broad.igv.util.ParsingUtils;
+import org.broad.igv.util.ResourceLocator;
 
-import java.util.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * Parses a ".mut" file
@@ -36,23 +42,57 @@ import java.util.*;
 public class MutationParser {
 
     private static Logger log = Logger.getLogger(MutationParser.class);
+    private int chrColumn;
+    private int startColumn;
+    private int endColumn;
+    private int sampleColumn;
+    private int typeColumn;
 
-    public static List<Track> loadMutationTracks(ResourceLocator locator) {
+    public static boolean isMutationAnnotationFile(ResourceLocator locator) throws IOException {
+        AsciiLineReader reader = null;
+        try {
+            reader = ParsingUtils.openAsciiReader(locator);
+            if (reader == null) {
+                return false;
+            }
+            String nextLine = reader.readLine();
+            if(nextLine == null) {
+                return false;
+            }
+            String[] tokens = nextLine.split("\t");
+            return tokens.length > 15 && tokens[0].equalsIgnoreCase("Hugo_Symbol");
+        }
+        catch (Exception e) {
+            log.error("", e);
+            throw new DataLoadException("Error checking for MAF file type: " + e.toString(), locator.getPath());
+        }
+        finally {
+            if (reader != null) {
+                reader.close();
+            }
+        }
+
+
+    }
+
+    public List<Track> loadMutationTracks(ResourceLocator locator) {
 
         List<Track> tracks = new ArrayList();
         Map<String, List<Feature>> features = loadMutations(locator);
-        for (String runId : features.keySet()) {
-            FeatureTrack track = new FeatureTrack(locator, runId, features.get(runId));
 
-            // Annotate the track as a "mutation" track. The only purpose of this
-            // property is to control popup menus, its an ugly hack that will be
-            // refactored out at some point.
-            track.setMutationTrack(true);
+        if (features.size() == 0) {
+            throw new DataLoadException(Globals.NO_FEATURES_FOUND_WARNING, locator.getPath());
+        }
+
+
+        for (String sampleId : features.keySet()) {
+
+            FeatureTrack track = new FeatureTrack(locator, new FeatureCollectionSource(features.get(sampleId)));
+            track.setHeight(15);
+            track.setName(sampleId);
 
             // Overrid default minimum height (10 for feature tracks).
             track.setMinimumHeight(0);
-
-            computeWholeGenome(track);
             tracks.add(track);
         }
         return tracks;
@@ -65,59 +105,68 @@ public class MutationParser {
      * @param locator
      * @return
      */
-    private static Map<String, List<Feature>> loadMutations(ResourceLocator locator) {
+    private Map<String, List<Feature>> loadMutations(ResourceLocator locator) {
         AsciiLineReader reader = null;
         String nextLine = null;
 
         try {
-            Genome genome = IGVModel.getInstance().getViewContext().getGenome();
+
+
+            Genome genome = ViewContext.getInstance().getGenome();
 
             reader = ParsingUtils.openAsciiReader(locator);
 
-            // Skip first line
-            reader.readLine();
+            // first line
+            String[] headers = reader.readLine().split("\t");
+            boolean isMAF = headers.length > 15 && headers[0].equalsIgnoreCase("Hugo_Symbol");
+            setColumns(headers, isMAF);
 
             Map<String, List<Feature>> mutationMap = new LinkedHashMap();
+            int lineNumber = 1;
             while ((nextLine = reader.readLine()) != null && (nextLine.trim().length() > 0)) {
+                lineNumber++;
                 String[] tokens = nextLine.split("\t");
                 if (tokens.length > 4) {
-                    String chr = genome.getChromosomeAlias(tokens[0].trim());
+                    String chr = genome.getChromosomeAlias(tokens[chrColumn].trim());
 
-                    int start = -1;
+                    int start;
                     try {
-                        start = Integer.parseInt(tokens[1].trim());
+                        start = Integer.parseInt(tokens[startColumn].trim());
+                        if (isMAF) {
+                            start--;
+                        }
                     } catch (NumberFormatException e) {
-                        throw new ParserException("Column 2 must be a numeric value.", reader.getCurrentLineNumber(), nextLine);
+                        throw new DataLoadException("Column " + (startColumn + 1) + " must be a numeric value.", locator.getPath());
                     }
 
-                    int end = -1;
+                    int end;
                     try {
-                        end = Integer.parseInt(tokens[2].trim());
+                        end = Integer.parseInt(tokens[endColumn].trim());
                     } catch (NumberFormatException e) {
-                        throw new ParserException("Column 3 must be a numeric value.", reader.getCurrentLineNumber(), nextLine);
+                        throw new DataLoadException("Column " + (endColumn + 1) + " must be a numeric value.", locator.getPath());
                     }
 
-                    String runId = tokens[3].trim();
-                    String typeString = tokens[4].trim().replace(' ', '_');
-
-                    Mutation.Type type;
-                    try {
-                        type = Mutation.Type.valueOf(typeString);
-                    } catch (Exception exception) {
-                        type = Mutation.Type.Unknown;
+                    String sampleId = tokens[sampleColumn].trim();
+                    String type = tokens[typeColumn];
+
+                    LinkedHashMap<String, String> attributes = new LinkedHashMap();
+                    int n = Math.min(headers.length, tokens.length);
+                    for (int i = 0; i < n; i++) {
+                        String key = headers[i];
+                        String value = tokens[i];
+                        if (value.length() > 0) {
+                            attributes.put(key, value);
+                        }
                     }
+                    Mutation mut = new Mutation(sampleId, chr, start, end, type);
+                    mut.setAttributes(attributes);
 
-                    List<Feature> features = mutationMap.get(runId);
+                    List<Feature> features = mutationMap.get(sampleId);
                     if (features == null) {
                         features = new ArrayList();
-                        mutationMap.put(runId, features);
+                        mutationMap.put(sampleId, features);
                     }
 
-                    Mutation mut = new Mutation(runId, chr, start, end, type);
-                    if (tokens.length > 14) {
-                        mut.setMClass(tokens[13].trim());
-                        mut.setNewBase(tokens[14].trim());
-                    }
 
                     features.add(mut);
                 }
@@ -125,54 +174,29 @@ public class MutationParser {
 
             return mutationMap;
         }
-        catch (ParserException e) {
-            throw e;
-        }
-        catch (Exception e) {
-            if (nextLine != null && reader.getCurrentLineNumber() != 0) {
-                throw new ParserException(e.getMessage(), e, reader.getCurrentLineNumber(), nextLine);
-            } else {
-                throw new RuntimeException(e);
-            }
-        }
-        finally {
+
+        catch (IOException e) {
+            log.error("", e);
+            throw new DataLoadException("IO Exception: " + e.toString(), locator.getPath());
+        } finally {
             reader.close();
         }
     }
 
-    /**
-     * Compute features for "chr All".  This is probably not the best way to do this,
-     * definitely not good for large tracks.  Need it now for mutation data.
-     */
-    public static void computeWholeGenome(FeatureTrack track) {
-
-        String genomeId = IGVModel.getInstance().getViewContext().getGenomeId();
-        int unit = 1000;  // KB
-
-        String chrAll = "All";
-        List<Feature> allFeatures = new ArrayList(1000);
-        Genome genome = GenomeManager.getInstance().getGenome(genomeId);
-        if (genome == null) {
-            throw new RuntimeException("Unknown genome: " + genomeId);
+    private void setColumns(String[] headings, boolean isMAF) {
+
+        if (isMAF) {
+            chrColumn = 4;
+            startColumn = 5;
+            endColumn = 6;
+            sampleColumn = 15;
+            typeColumn = 8;
+        } else {
+            chrColumn = 0;
+            startColumn = 1;
+            endColumn = 2;
+            sampleColumn = 3;
+            typeColumn = 4;
         }
-        genome.getChromosomeNames();
-        long offset = 0;
-        for (String chr : genome.getChromosomeNames()) {
-            int chrLength = genome.getChromosome(chr).getLength();
-            Collection<Feature> features = track.getFeatures(chr, 0, chrLength);
-            if (features != null) {
-                for (Feature f : features) {
-                    Mutation m = (Mutation) f;
-                    int start = (int) ((offset + m.getStart()) / unit);
-                    int end = (int) ((offset + m.getEnd()) / unit);
-                    String runId = m.getRunId();
-                    Mutation.Type type = m.getMutationType();
-                    allFeatures.add(new Mutation(runId, chrAll, start, end, type));
-                }
-            }
-            offset += chrLength;
-        }
-
-        track.setFeatures(chrAll, allFeatures);
     }
 }
diff --git a/src/org/broad/igv/feature/PSLParser.java b/src/org/broad/igv/feature/PSLParser.java
new file mode 100644
index 0000000..5c2fb4a
--- /dev/null
+++ b/src/org/broad/igv/feature/PSLParser.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.feature;
+
+import org.broad.igv.util.ParsingUtils;
+import org.broad.igv.util.ResourceLocator;
+
+/**
+ * 1. matches - Number of bases that match that aren't repeats
+ * 2. misMatches - Number of bases that don't match
+ * 3. repMatches - Number of bases that match but are part of repeats
+ * 4. nCount - Number of 'N' bases
+ * 5. qNumInsert - Number of inserts in query
+ * 6. qBaseInsert - Number of bases inserted in query
+ * 7. tNumInsert - Number of inserts in target
+ * 8. tBaseInsert - Number of bases inserted in target
+ * 9. strand - '+' or '-' for query strand. For translated alignments, second '+'or '-' is for genomic strand
+ * 10. qName - Query sequence name
+ * 11. qSize - Query sequence size
+ * 12. qStart - Alignment start position in query
+ * 13. qEnd - Alignment end position in query
+ * 14. tName - Target sequence name
+ * 15. tSize - Target sequence size
+ * 16. tStart - Alignment start position in target
+ * 17. tEnd - Alignment end position in target
+ * 18. blockCount - Number of blocks in the alignment (a block contains no gaps)
+ * 19. blockSizes - Comma-separated list of sizes of each block
+ * 20. qStarts - Comma-separated list of starting positions of each block in query
+ * 21. tStarts - Comma-separated list of starting positions of each block in target
+ * <p/>
+ * Example:
+ * track name=fishBlats description="Fish BLAT" useScore=1
+ * 59 9 0 0 1 823 1 96 +- FS_CONTIG_48080_1 1955 171 1062 chr22 47748585 13073589 13073753 2 48,20,  171,1042,  34674832,34674976,
+ * 59 7 0 0 1 55 1 55 +- FS_CONTIG_26780_1 2825 2456 2577 chr22 47748585 13073626 13073747 2 21,45,  2456,2532,  34674838,34674914,
+ * 59 7 0 0 1 55 1 55 -+ FS_CONTIG_26780_1 2825 2455 2576 chr22 47748585 13073627 13073748 2 45,21,  249,349,  13073627,13073727,
+ *
+ * @author jrobinso
+ * @date Aug 5, 2010
+ */
+public class PSLParser extends UCSCParser {
+
+    @Override
+    public boolean isFeatureFile(ResourceLocator locator) {
+        String path = locator.getPath();
+        return path.endsWith(".psl") || path.endsWith(".psl.gz")  ||
+                path.endsWith(".pslx") || path.endsWith(".pslx.gz");
+    }
+
+    @Override
+    protected Feature parseLine(String[] tokens, int nTokens) {
+
+        if (nTokens < 21) {
+            // log.info("Skipping line ")
+            return null;
+        }
+        int tSize = Integer.parseInt(tokens[14]);
+        String chr = tokens[13];
+        int start = Integer.parseInt(tokens[15]); // IS PSL 1 or ZERO based,  closed or open?
+
+        String strandString = tokens[8];
+        Strand strand = strandString.startsWith("+") ? Strand.POSITIVE : Strand.NEGATIVE;
+
+        boolean gNeg = false;
+        if (strandString.length() > 1) {
+            gNeg = strandString.charAt(1) == '-';
+        }
+
+        BasicFeature f = new BasicFeature();
+        f.setName(tokens[9]);
+        f.setChr(chr);
+        f.setStart(start);
+        f.setEnd(Integer.parseInt(tokens[16]));
+        f.setStrand(strand);
+
+        int exonCount = Integer.parseInt(tokens[17]);
+        String[] exonSizes = tokens[18].split(",");
+        String[] startsBuffer = tokens[20].split(",");
+
+        if (startsBuffer.length == exonSizes.length && exonCount == startsBuffer.length) {
+            for (int i = 0; i < startsBuffer.length; i++) {
+                int exonSize = Integer.parseInt(exonSizes[i]);
+                int exonStart = Integer.parseInt(startsBuffer[i]);
+                if (gNeg) {
+                    exonStart = tSize - exonStart - exonSize;
+                }
+                int exonEnd = exonStart + exonSize;
+                Exon exon = new Exon(chr, exonStart, exonEnd, strand);
+                f.addExon(exon);
+            }
+        } else {
+            // TODO -- warn
+        }
+
+
+        return f;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+}
diff --git a/src/org/broad/igv/feature/ProbeToGeneMap.java b/src/org/broad/igv/feature/ProbeToGeneMap.java
index e161e3d..2678ab8 100644
--- a/src/org/broad/igv/feature/ProbeToGeneMap.java
+++ b/src/org/broad/igv/feature/ProbeToGeneMap.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -19,13 +19,13 @@ package org.broad.igv.feature;
 
 //~--- non-JDK imports --------------------------------------------------------
 
-import cern.colt.map.OpenIntObjectHashMap;
 import org.apache.log4j.Logger;
 import org.broad.igv.PreferenceManager;
 import org.broad.igv.exceptions.LoadResourceFromServerException;
-import org.broad.igv.ui.IGVModel;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.ui.util.MessageUtils;
 import org.broad.igv.util.AsciiLineReader;
+import org.broad.igv.util.IGVHttpUtils;
 import org.broad.igv.util.ParsingUtils;
 
 import java.io.FileInputStream;
@@ -116,15 +116,16 @@ public class ProbeToGeneMap {
      * @param map
      * @throws Exception
      */
-    public void loadMapping(String urlString, Map<String, String[]> map) {
+    public void loadMapping(String urlString, Map<String, String[]> map)  {
         AsciiLineReader bufReader = null;
+        InputStream is = null;
         try {
-            InputStream is = null;
 
-            if (urlString.startsWith("http") || urlString.startsWith("file")) {
+
+            if (urlString.startsWith("http:") || urlString.startsWith("https:") ||
+                     urlString.startsWith("file:")) {
                 URL url = new URL(urlString);
-                URLConnection connection = url.openConnection();
-                is = connection.getInputStream();
+                is = IGVHttpUtils.openConnectionStream(url);
             } else {
                 is = new FileInputStream(urlString);
             }
@@ -140,8 +141,12 @@ public class ProbeToGeneMap {
 
             throw new LoadResourceFromServerException(e.getMessage(), urlString, e.getClass().getSimpleName());
         } finally {
-            if (bufReader != null) {
-                bufReader.close();
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    log.error("Error closing probe mapping stream", e);
+                }
             }
         }
     }
@@ -183,11 +188,9 @@ public class ProbeToGeneMap {
             if (!mapToGenes) {
                 boolean hasLociMapping = checkForLociMapping(platform, genomeId);
                 if (!hasLociMapping) {
-                    Genome genome = GenomeManager.getInstance().getGenome(genomeId);
-                    String genomeName = genome.getId();
                     MessageUtils.showMessage(
                             "<html>" + platform.toString() + " probe locations are not available for the selected genome " +
-                                    " (" + genomeName + "). <br>Expression data will be mapped to gene locations.");
+                                    " (" + genomeId + "). <br>Expression data will be mapped to gene locations.");
                     mapToGenes = true;
                 }
             }
@@ -282,23 +285,15 @@ public class ProbeToGeneMap {
 
     public String[] getGenesForProbe(String probeId) {
 
-        String genomeId = IGVModel.getInstance().getViewContext().getGenomeId();
+        String genomeId = ViewContext.getInstance().getGenomeId();
         Platform platform = getPlatform(probeId);
 
-        if (log.isDebugEnabled()) {
-            log.debug("Searching probeToGene map for platform " + platform.toString());
-        }
-
         if (platform == Platform.unknown) {
             return null;
         }
 
         String mappingURL = getMappingURL(genomeId, platform);
 
-        if (log.isDebugEnabled()) {
-            log.debug("mappingURL = " + mappingURL);
-        }
-
         if (mappingURL == null) {
             return null;
         }
@@ -344,10 +339,4 @@ public class ProbeToGeneMap {
         }
     }
 
-    public static void main(String[] args) throws Exception {
-        OpenIntObjectHashMap map = new OpenIntObjectHashMap(1000000);
-        //ProbeToGeneMap.getInstance().loadMapping(ProbeToGeneMap.agilentMappingURL, map);
-        //System.out.println("sz = " + map.size());
-        //System.in.read();
-    }
 }
diff --git a/src/org/broad/igv/feature/SequenceManager.java b/src/org/broad/igv/feature/SequenceManager.java
index b4a1880..04a6902 100644
--- a/src/org/broad/igv/feature/SequenceManager.java
+++ b/src/org/broad/igv/feature/SequenceManager.java
@@ -46,6 +46,53 @@ public class SequenceManager {
     private static ObjectCache<String, SequenceChunk> sequenceCache = new ObjectCache(50);
 
     /**
+     * Return the sequence in Color Space (SOLID alignment encoding)
+     *
+     * @param genome
+     * @param chr
+     * @param start
+     * @param end
+     * @return
+     */
+    public static byte[] readCSSequence(String genome, String chr, int start, int end) {
+        // We need to know the base just to the left of the start
+        int csStart = (start == 0 ? 0 : start - 1);
+        byte[] baseSequence = readSequence(genome, chr, csStart, end);
+        if (baseSequence == null || baseSequence.length == 0) {
+            return baseSequence;
+        }
+
+        byte[] csSequence = new byte[end - start];
+        int i = 0;
+        int c1 = start == 0 ? 0 : baseToCS(baseSequence[i++]);
+        for (; i < baseSequence.length; i++) {
+            int c2 = baseToCS(baseSequence[i]);
+            csSequence[i] = (byte) (c1 ^ c2);
+        }
+        return csSequence;
+
+    }
+
+    private static int baseToCS(byte base) {
+        switch (base) {
+            case 'A':
+            case 'a':
+                return 0;
+            case 'C':
+            case 'c':
+                return 1;
+            case 'T':
+            case 't':
+                return 2;
+            case 'G':
+            case 'g':
+                return 3;
+        }
+        return -1;
+    }
+
+
+    /**
      * Return the reference dna sequence for the exact interval specified.
      *
      * @param genome
@@ -63,15 +110,14 @@ public class SequenceManager {
             if (descriptor != null) {
                 String location = descriptor.getSequenceLocation();
                 if (location != null) {
-                    location = convertSequenceURL(location);
+                    location = convertSequenceURL(location, genome);
                     return getSequence(descriptor, chr, start, end, location);
-
                 }
             }
         } catch (Exception e) {
             log.error(e.getMessage(), e);
-        }
 
+        }
         return new byte[0];
     }
 
@@ -116,7 +162,7 @@ public class SequenceManager {
 
             return sequence;
         } else {
-            return readSequence(genome, chr, location, start, end);
+            return readSequence(genome, chr, start, end, location);
         }
     }
 
@@ -129,7 +175,7 @@ public class SequenceManager {
             int start = tileNo * chunkSize;
             int end = start + chunkSize; // <=  UCSC coordinate conventions (end base not inclusive)
 
-            byte[] seq = readSequence(genome, chr, location, start, end);
+            byte[] seq = readSequence(genome, chr, start, end, location);
             chunk = new SequenceChunk(start, seq);
             sequenceCache.put(key, chunk);
         }
@@ -137,7 +183,7 @@ public class SequenceManager {
         return chunk;
     }
 
-    private static byte[] readSequence(GenomeDescriptor genome, String chr, String location, int start, int end) {
+    private static byte[] readSequence(GenomeDescriptor genome, String chr, int start, int end, String location) {
 
         if (location.startsWith("http://www.broadinstitute.org/igv/SequenceServlet") ||
                 location.startsWith("http://www.broadinstitute.org/igv/sequence")) {
@@ -150,16 +196,14 @@ public class SequenceManager {
                     location = location + "/";
                 }
 
-
                 String fn = chr + ".txt";
                 if (genome.isChrNamesAltered()) {
                     fn = getChrFileName(fn);
                 }
+
                 String seqFile = location + fn;
 
-                is = seqFile.startsWith("http:") ?
-                        new SeekableHTTPStream(new URL(seqFile)) :
-                        new SeekableFileStream(new File(seqFile));
+                is = SeekableStreamFactory.getStreamFor(seqFile);
 
                 byte[] bytes = new byte[end - start];
                 is.seek(start);
@@ -184,10 +228,6 @@ public class SequenceManager {
         }
     }
 
-    static String getKey(String genome, String chr, int tileNo) {
-        return genome + chr + tileNo;
-    }
-
     private static Map<String, String> chrFileNameCache = new HashMap();
 
     private static String getChrFileName(String fn) {
@@ -199,6 +239,9 @@ public class SequenceManager {
         return chrFN;
     }
 
+    static String getKey(String genome, String chr, int tileNo) {
+        return genome + chr + tileNo;
+    }
 
     /**
      * This accessor provided to support unit tests.
@@ -252,7 +295,7 @@ public class SequenceManager {
      * Also modifies URLs to Broad hosted sequences that will use byte range requests if byte-range requests are
      * disabled.  This hack is neccessary for the Partners network, which does not forward the byte-range header.
      * <p/>
-     * Older "sequence servlet" request URLs (servlets)
+     * Older "sequence servlet" request URLs
      * http://www.broad.mit.edu/igv/SequenceServlet/
      * http://www.broadinstitute.org/igv/sequence
      * <p/>
@@ -267,23 +310,37 @@ public class SequenceManager {
     private static Hashtable<String, String> sequenceUrlCache = new Hashtable();
 
 
-    public static String convertSequenceURL(String url) {
+    public static String convertSequenceURL(String url, String genome) {
 
-        final boolean useByteRange = PreferenceManager.getInstance().isUseByteRange();
+        final boolean useByteRange = IGVHttpUtils.useByteRange();
         String key = url + useByteRange;
         String convertedURL = sequenceUrlCache.get(key);
         if (convertedURL == null) {
             convertedURL = url;
             // Legacy URLs -- this code can be removed when all .genome files are updated.
-            convertedURL = convertedURL.replace("broad.mit.edu", "broadinstitute.org");
-            convertedURL = convertedURL.replace("http://www.broadinstitute.org/igv/SequenceServlet", "http://www.broadinstitute.org/igvdata/annotations/seq");
+            convertedURL = convertedURL.replace(
+                    "broad.mit.edu",
+                    "broadinstitute.org");
+
+
+            convertedURL = convertedURL.replace(
+                    "http://www.broadinstitute.org/igv/SequenceServlet",
+                    "http://www.broadinstitute.org/igvdata/annotations/seq");
 
 
             if (!useByteRange) {
                 // Translate our URLS to use the servlet
-                convertedURL = convertedURL.replace("http://www.broadinstitute.org/igvdata/annotations/seq", "http://www.broadinstitute.org/igv/sequence");
-                convertedURL = convertedURL.replace("http://igvdata.broadinstitute.org/genomes/seq", "http://www.broadinstitute.org/igv/sequence");
-                convertedURL = convertedURL.replace("http://igv.broadinstitute.org/genomes/seq", "http://www.broadinstitute.org/igv/sequence");
+                convertedURL = convertedURL.replace(
+                        "http://www.broadinstitute.org/igvdata/annotations/seq",
+                        "http://www.broadinstitute.org/igv/sequence");
+
+                convertedURL = convertedURL.replace(
+                        "http://igvdata.broadinstitute.org/genomes/seq",
+                        "http://www.broadinstitute.org/igv/sequence");
+
+                convertedURL = convertedURL.replace(
+                        "http://igv.broadinstitute.org/genomes/seq",
+                        "http://www.broadinstitute.org/igv/sequence");
             }
 
 
@@ -298,18 +355,5 @@ public class SequenceManager {
 
     }
 
-    private static Set<String> cloudHostedSequences = new HashSet<String>(Arrays.asList("hg18", "hg19", "mm9", "mm8"));
-
-    // TODO -- move to another util class
-
 
-    private static String getHostName() {
-        try {
-            InetAddress addr = InetAddress.getLocalHost();
-            return addr.getHostName();
-        } catch (UnknownHostException e) {
-            return null;
-        }
-
-    }
 }
diff --git a/src/org/broad/igv/feature/SnpFileParser.java b/src/org/broad/igv/feature/SnpFileParser.java
index 62e7a7b..1866804 100644
--- a/src/org/broad/igv/feature/SnpFileParser.java
+++ b/src/org/broad/igv/feature/SnpFileParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/feature/Strand.java b/src/org/broad/igv/feature/Strand.java
index f54c09c..d8d7419 100644
--- a/src/org/broad/igv/feature/Strand.java
+++ b/src/org/broad/igv/feature/Strand.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/feature/TribbleFeature.java b/src/org/broad/igv/feature/TribbleFeature.java
new file mode 100644
index 0000000..c40ebf5
--- /dev/null
+++ b/src/org/broad/igv/feature/TribbleFeature.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.feature;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: Jan 13, 2010
+ * Time: 4:14:56 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public interface TribbleFeature {
+
+    public String getChr();
+
+    public int getStart();
+
+    public int getEnd();
+
+
+}
diff --git a/src/org/broad/igv/feature/UCSCGeneTableParser.java b/src/org/broad/igv/feature/UCSCGeneTableParser.java
index c047775..e0f571b 100644
--- a/src/org/broad/igv/feature/UCSCGeneTableParser.java
+++ b/src/org/broad/igv/feature/UCSCGeneTableParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -20,7 +20,6 @@ package org.broad.igv.feature;
 //~--- JDK imports ------------------------------------------------------------
 
 import org.broad.igv.util.ParsingUtils;
-import org.broad.igv.ui.IGVModel;
 
 import java.util.List;
 
@@ -99,9 +98,9 @@ public class UCSCGeneTableParser extends UCSCParser {
             name = identifier;
         }
 
-
-        String chr = genome.getChromosomeAlias(tokens[chrColumn].trim());
-        int start = Integer.parseInt(tokens[startColumn]);
+        String chrToken = tokens[chrColumn].trim();
+        String chr = genome == null ? chrToken : genome.getChromosomeAlias(chrToken);
+        int start = Integer.parseInt(tokens[startColumn]) - startBase;
         int end = Integer.parseInt(tokens[endColumn]);
         String strandString = tokens[strandColumn];
         Strand strand = Strand.NONE;
@@ -130,7 +129,7 @@ public class UCSCGeneTableParser extends UCSCParser {
                              Strand strand)
             throws NumberFormatException {
 
-        int cdStart = Integer.parseInt(tokens[cdStartColumn]);
+        int cdStart = Integer.parseInt(tokens[cdStartColumn])  - startBase;
         int cdEnd = Integer.parseInt(tokens[cdEndColumn]);
 
         int exonCount = Integer.parseInt(tokens[exonCountColumn]);
@@ -143,7 +142,7 @@ public class UCSCGeneTableParser extends UCSCParser {
         if (startsBuffer.length == endsBuffer.length) {
             int exonNumber = (strand == Strand.NEGATIVE ? exonCount : 1);
             for (int i = 0; i < startsBuffer.length; i++) {
-                int exonStart = Integer.parseInt(startsBuffer[i]);
+                int exonStart = Integer.parseInt(startsBuffer[i])  - startBase;
                 int exonEnd = Integer.parseInt(endsBuffer[i]);
                 Exon exon = new Exon(chr, exonStart, exonEnd, strand);
                 exon.setCodingStart(cdStart);
@@ -161,12 +160,12 @@ public class UCSCGeneTableParser extends UCSCParser {
         List<Exon> exons = gene.getExons();
 
         // Walk through exons setting mRNA start position
-        int start = strand == Strand.POSITIVE  ? 0 : exonCount - 1;
-        int end = strand == Strand.POSITIVE  ? exonCount  : -1;
-        int increment = strand == Strand.POSITIVE  ? 1  : -1;
+        int start = strand == Strand.POSITIVE ? 0 : exonCount - 1;
+        int end = strand == Strand.POSITIVE ? exonCount : -1;
+        int increment = strand == Strand.POSITIVE ? 1 : -1;
 
         int mRNABase = 0;
-        for(int i=start; i != end; i += increment) {
+        for (int i = start; i != end; i += increment) {
             Exon exon = exons.get(i);
             exon.setMrnaBase(mRNABase);
             mRNABase += exon.getCodingLength();
@@ -190,6 +189,7 @@ public class UCSCGeneTableParser extends UCSCParser {
             } catch (Exception e) {
 
                 // Ignore -- not getting the reading frame is not the end of the world.
+                //log.info("Could not parse frame buffer: ", e);
             }
 
         }
diff --git a/src/org/broad/igv/feature/UCSCParser.java b/src/org/broad/igv/feature/UCSCParser.java
index c9f361e..e8c0074 100644
--- a/src/org/broad/igv/feature/UCSCParser.java
+++ b/src/org/broad/igv/feature/UCSCParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -36,8 +36,9 @@ abstract public class UCSCParser extends AbstractFeatureParser {
      * @param nextLine
      * @return
      */
+    String[] tokens = new String[25];
+    
     protected Feature parseLine(String nextLine) {
-        String[] tokens = new String[20];
 
         if (nextLine.startsWith("#")) {
 
diff --git a/src/org/broad/igv/feature/dranger/DRangerFeature.java b/src/org/broad/igv/feature/dranger/DRangerFeature.java
index 2f8fdfd..7642f34 100644
--- a/src/org/broad/igv/feature/dranger/DRangerFeature.java
+++ b/src/org/broad/igv/feature/dranger/DRangerFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -26,96 +26,247 @@ import org.broad.igv.feature.AbstractFeature;
 import org.broad.igv.feature.LocusScore;
 import org.broad.igv.feature.Strand;
 import org.broad.igv.track.WindowFunction;
+import org.broad.igv.util.ColorUtilities;
+
+import java.awt.*;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * @author jrobinso
  */
 public class DRangerFeature extends AbstractFeature {
 
-    int index;
-    int tumorReads;
-    int count;
-    DRangerFeature mate;
-    private String site = "";
-    private String gene = "";
+    //   //num	chr1	str1	pos1	chr2	str2	pos2	tumreads	normreads	class	span	site1	site2	quality	score
 
+    private int index;
 
-    public DRangerFeature(String chr, int start, int end, Strand strand, int tumReads, int count) {
-        super(chr, start, end, strand);
-        this.tumorReads = tumReads;
-        this.count = count;
-    }
+    String chr2;
+    int pos2;
+    Strand str2;
+
+    private int tumreads;
+    private int normreads;
+    private String featureClass;
+    private int span;
+    private String site1;
+    private String site2;
+    private int quality;
+    private int score;
+
+    public DRangerFeature() {
 
-    private DRangerFeature(DRangerFeature f) {
-        super(f.getChromosome(), f.getStart(), f.getEnd(), f.getStrand());
-        this.index = f.index;
-        this.tumorReads = f.tumorReads;
-        this.count = f.count;
-        this.mate = f.mate;
     }
 
+    /**
+     * Construct a new dRanger feature.  Arbitrarily sized at 10 bases so they are easier to see
+     *
+     * @param chr
+     * @param pos1
+     * @param strand1
+     * @param chr2
+     * @param pos2
+     * @param str2
+     */
+
+    public DRangerFeature(String chr, int pos1, Strand strand1, String chr2, int pos2, Strand str2) {
+        super(chr, pos1 - 5, pos1 + 5, strand1);
+        this.chr2 = chr2;
+        this.pos2 = pos2;
+        this.str2 = str2;
+    }
 
     @Override
     public boolean hasScore() {
         return true;
     }
 
+    public String getURL() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
     // Score calibrated to = ~ 1,000 if 1/2 the reads support the rearrangment
+
     @Override
     public float getScore() {
-        return count == 0 ? 0.0f : 2000.0f * 20.0f * tumorReads / count;
+        return score;
 
     }
 
     public LocusScore copy() {
-        return new DRangerFeature(this);
+        DRangerFeature newFeat = new DRangerFeature();
+        newFeat.setChr(getChr());
+        newFeat.setStart(getStart());
+        newFeat.setEnd(getEnd());
+        newFeat.chr2 = chr2;
+        newFeat.pos2 = pos2;
+        newFeat.str2 = str2;
+        newFeat.tumreads = tumreads;
+        newFeat.normreads = normreads;
+        newFeat.featureClass = featureClass;
+        ;
+        newFeat.span = span;
+        newFeat.site1 = site1;
+        newFeat.site2 = site2;
+        newFeat.quality = quality;
+        newFeat.score = score;
+        return newFeat;
     }
 
     public String positionString() {
-        return getChromosome() + ":" + getStart() + "-" + getEnd();
+        return getChr() + ":" + getStart() + "-" + getEnd();
     }
 
-    public String getValueString(double position, WindowFunction windowFunction) {
-        StringBuffer buf = new StringBuffer();
-        buf.append("Index:   " + index + "<br>");
-        buf.append("Region:  " + positionString() + "<br>");
-        buf.append("Site:    " + site + "<br>");
-        buf.append("Gene:    " + gene + "<br><br>");
-        buf.append("Mate:    " + mate.positionString() + "<br>");
-        buf.append("Site:    " + mate.getSite() + "<br>");
-        buf.append("Gene:    " + mate.getGene() + "<br><br>");
-        buf.append("T reads: " + tumorReads + "<br>");
-        buf.append("Score:   " + getScore());
-
-        return buf.toString();
-
-    }
 
     /**
      * @return the site
      */
     public String getSite() {
-        return site;
+        return site1;
     }
 
     /**
-     * @param site the site to set
+     * @param site1 the site to set
      */
-    public void setSite(String site) {
-        this.site = site;
+    public void setSite1(String site1) {
+        this.site1 = site1;
     }
 
-    /**
-     * @return the gene
-     */
-    public String getGene() {
-        return gene;
+
+    public int getIndex() {
+        return index;
     }
 
-    /**
-     * @param gene the gene to set
-     */
-    public void setGene(String gene) {
-        this.gene = gene;
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+    public int getTumreads() {
+        return tumreads;
+    }
+
+    public void setTumreads(int tumreads) {
+        this.tumreads = tumreads;
+    }
+
+    public int getNormreads() {
+        return normreads;
+    }
+
+    public void setNormreads(int normreads) {
+        this.normreads = normreads;
+    }
+
+    public String getFeatureClass() {
+        return featureClass;
+    }
+
+    public void setFeatureClass(String featureClass) {
+        this.featureClass = featureClass;
+    }
+
+    public int getSpan() {
+        return span;
+    }
+
+    public void setSpan(int span) {
+        this.span = span;
+        //setEnd(getStart() + span);
+    }
+
+    public String getSite1() {
+        return site1;
+    }
+
+    public String getSite2() {
+        return site2;
+    }
+
+    public void setSite2(String site2) {
+        this.site2 = site2;
+    }
+
+    public int getQuality() {
+        return quality;
+    }
+
+    public void setQuality(int quality) {
+        this.quality = quality;
+    }
+
+    public void setScore(int score) {
+        this.score = score;
+    }
+
+
+    float[] defaultColor = Color.blue.getComponents(null);
+    static Map<String, float[]> colorMap;
+
+    static {
+        colorMap = new HashMap();
+        colorMap.put("deletion", Color.red.getComponents(null));
+        colorMap.put("inter_chr", Color.blue.getComponents(null));
+        colorMap.put("inversion", Color.green.getComponents(null));
+        colorMap.put("long_range", Color.MAGENTA.getComponents(null));
+        colorMap.put("tandem_dup", Color.cyan.getComponents(null));
+    }
+
+    @Override
+    public Color getColor() {
+        float[] baseColor = defaultColor;
+        if (featureClass != null && colorMap.containsKey(featureClass)) {
+            baseColor = colorMap.get(featureClass);
+        }
+        float alpha = Math.min(1.0f, Math.max(0.15f, .15f + (.85f / 3) * score));
+        return ColorUtilities.getCompositeColor(baseColor, alpha);
+    }
+
+
+    /*
+       int index;
+   int tumorReads;
+   int normalReads;
+   private String featureClass;
+   private int span;
+   private String site = "";
+   int quality;
+   int score;
+    */
+    //num	chr1	str1	pos1	chr2	str2	pos2	tumreads	normreads	class	span
+
+    // site1	site2	quality	score
+
+    public String getValueString(double position, WindowFunction windowFunction) {
+        StringBuffer buf = new StringBuffer();
+        if (index > 0) {
+            buf.append("Index:   " + index + "<br>");
+        }
+        if (site1 != null) {
+            buf.append("Site1:    " + site1 + "<br>");
+        }
+        if (site2 != null) {
+            buf.append("Site2:    " + site2 + "<br>");
+        }
+        if (tumreads > 0) {
+            buf.append("T reads: " + tumreads + "<br>");
+        }
+        if (normreads > 0) {
+            buf.append("N reads: " + normreads + "<br>");
+        }
+        if (featureClass != null) {
+            buf.append("Class: " + featureClass + "<br>");
+        }
+        if (span > 0) {
+            buf.append("Span:    " + span + "<br>");
+        }
+        if (quality > 0) {
+            buf.append("Quality:   " + quality + "<br>");
+        }
+        if (score > 0) {
+            buf.append("Score:   " + score);
+        }
+
+        return buf.toString();
+
     }
 }
diff --git a/src/org/broad/igv/feature/dranger/DRangerParser.java b/src/org/broad/igv/feature/dranger/DRangerParser.java
index 190d3c1..fb90575 100644
--- a/src/org/broad/igv/feature/dranger/DRangerParser.java
+++ b/src/org/broad/igv/feature/dranger/DRangerParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -25,13 +25,14 @@ package org.broad.igv.feature.dranger;
 import org.apache.log4j.Logger;
 import org.broad.igv.exceptions.ParserException;
 import org.broad.igv.feature.Feature;
-import org.broad.igv.util.ParsingUtils;
-import org.broad.igv.feature.Strand;
 import org.broad.igv.feature.Genome;
+import org.broad.igv.feature.Strand;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.track.FeatureCollectionSource;
 import org.broad.igv.track.FeatureTrack;
 import org.broad.igv.util.AsciiLineReader;
+import org.broad.igv.util.ParsingUtils;
 import org.broad.igv.util.ResourceLocator;
-import org.broad.igv.ui.IGVModel;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -42,28 +43,28 @@ import java.util.Map;
  * @author jrobinso
  */
 public class DRangerParser {
+    int numColumn;
+    int chr1Column;
+    int str1Column;
+    int pos1Column;
+    int chr2Column;
+    int str2Column;
+    int pos2Column;
+    int tumreadsColumn;
+    int normreadsColumn;
+    int classColumn;
+    int spanColumn;
+    int site1Column;
+    int site2Column;
+    int qualityColumn;
+    int scoreColumn;
 
-    int CHR1 = 1;
-    int CHR2 = 6;
-    int COUNT1 = 26;
-    int COUNT2 = 27;
-    int FILTER = 43;
-    int FILTERC = 31;
-    int FILTERH = 34;
-    int FILTERL = 35;
-    int MAX1 = 5;
-    int MAX2 = 10;
-    int MIN1 = 4;
-    int MIN2 = 9;
-    int STR1 = 2;
-    int STR2 = 7;
-    int TUMREADS = 11;
-    int SITE1 = -1;
-    int GENE1 = -1;
-    int SITE2 = -1;
-    int GENE2 = -1;
     static Logger log = Logger.getLogger(DRangerParser.class);
 
+    //num	chr1	str1	pos1	chr2	str2	pos2
+    //1	    chr1	(+)	21083863	chr1	(+)	25279578
+    // tumreads	normreads	class	span	site1	site2	quality	score
+    // 48	    0	    long_range	4195715	3'-UTR of ENST0000037493
     public List<FeatureTrack> loadTracks(ResourceLocator locator) {
 
         String[] tokens = new String[100];
@@ -74,72 +75,56 @@ public class DRangerParser {
         int parseColumn = -1;
         String nextLine = null;
         try {
-            Genome genome = IGVModel.getInstance().getViewContext().getGenome();
-            
-            reader = ParsingUtils.openAsciiReader(locator);
+            Genome genome = ViewContext.getInstance().getGenome();
 
+            reader = ParsingUtils.openAsciiReader(locator);
             setColumns(reader.readLine());
             while ((nextLine = reader.readLine()) != null) {
                 int nTokens = ParsingUtils.split(nextLine, tokens, '\t');
-                if (nTokens > FILTER) {
-                    int filter = Integer.parseInt(tokens[FILTER]);
-
-                    if (filter != 1) {
-                        String chr = genome.getChromosomeAlias(tokens[CHR1]);
-
-                        parseColumn = CHR1 + 1;
-                        int index = Integer.parseInt(tokens[CHR1]);
-                        parseColumn = MIN1 + 1;
-                        int start = Integer.parseInt(tokens[MIN1]);
-                        parseColumn = MAX1 + 1;
-                        int end = Integer.parseInt(tokens[MAX1]);
-                        parseColumn = STR1 + 1;
-                        Strand strand = Integer.parseInt(tokens[STR1]) == 0 ? Strand.POSITIVE : Strand.NEGATIVE;
-                        parseColumn = TUMREADS + 1;
-                        int tumreads = Integer.parseInt(tokens[TUMREADS]);
-                        parseColumn = COUNT1 + 1;
-                        int count1 = Integer.parseInt(tokens[COUNT1]);
-                        parseColumn = COUNT2 + 1;
-                        int count2 = Integer.parseInt(tokens[COUNT2]);
-                        int minCount = Math.min(count1, count2);
-
-                        String mateChr = genome.getChromosomeAlias(tokens[CHR2]);
-                        parseColumn = MIN2 + 1;
-                        int mateStart = Integer.parseInt(tokens[MIN2]);
-                        parseColumn = MAX2 + 1;
-                        int mateEnd = Integer.parseInt(tokens[MAX2]);
-
-                        parseColumn = STR2 + 1;
-                        Strand mateStrand = Integer.parseInt(tokens[STR2]) == 0 ? Strand.POSITIVE : Strand.NEGATIVE;
-
-                        DRangerFeature feature = new DRangerFeature(chr, start, end, strand, tumreads, minCount);
-                        DRangerFeature mate = new DRangerFeature(mateChr, mateStart, mateEnd, mateStrand, tumreads, minCount);
-
-                        if (SITE1 > 0) {
-                            feature.setSite(tokens[SITE1]);
-                        }
-                        if (SITE2 > 0) {
-                            mate.setSite(tokens[SITE2]);
-                        }
-                        if (GENE1 > 0) {
-                            feature.setSite(tokens[GENE1]);
-                        }
-                        if (GENE2 > 0) {
-                            feature.setSite(tokens[GENE2]);
-                        }
-
-                        feature.index = index;
-                        feature.mate = mate;
-                        mate.index = index;
-                        mate.mate = feature;
-
-                        features.add(feature);
-                        features.add(mate);
+                if (nTokens > pos2Column) {
+                    int index = Integer.parseInt(tokens[numColumn]);
+
+                    String chr1 = genome.getChromosomeAlias(tokens[chr1Column]);
+                    int pos1 = Integer.parseInt(tokens[pos1Column]);
+                    String str1 = tokens[str1Column];
+                    Strand strand1 = (str1.equals("0") || str1.equals("(+)")) ? Strand.POSITIVE : Strand.NEGATIVE;
+
+                    String chr2 = genome.getChromosomeAlias(tokens[chr2Column]);
+                    int pos2 = Integer.parseInt(tokens[pos2Column]);
+                    String str2 = tokens[str2Column];
+                    Strand strand2 = (str2.equals("0") || str2.equals("(+)")) ? Strand.POSITIVE : Strand.NEGATIVE;
+
+                    DRangerFeature feature = new DRangerFeature(chr1, pos1, strand1, chr2, pos2, strand2);
+                    feature.setIndex(index);
+
+                    if (tumreadsColumn > 0) {
+                        feature.setTumreads(toInt(tokens[tumreadsColumn]));
+                    }
+                    if (normreadsColumn > 0) {
+                        feature.setNormreads(toInt(tokens[normreadsColumn]));
+                    }
+                    if (classColumn > 0) {
+                        feature.setFeatureClass(tokens[classColumn]);
+                    }
+                    if (spanColumn > 0) {
+                        feature.setSpan(toInt(tokens[spanColumn]));
+                    }
+                    if (site1Column > 0) {
+                        feature.setSite1(tokens[site1Column]);
+                    }
+                    if (site2Column > 0) {
+                        feature.setSite2(tokens[site2Column]);
+                    }
+                    if (qualityColumn > 0) {
+                        feature.setQuality(toInt(tokens[qualityColumn]));
                     }
+                    if (scoreColumn > 0) {
+                        feature.setScore(toInt(tokens[scoreColumn]));
+                    }
+
+                    features.add(feature);
                 }
             }
-
-
         }
         catch (NumberFormatException ne) {
             throw new ParserException("Column " + parseColumn + " must be a numeric value", reader.getCurrentLineNumber(), nextLine);
@@ -158,47 +143,65 @@ public class DRangerParser {
             }
         }
 
-        String baseName = locator.getDisplayName();
-        FeatureTrack track = new FeatureTrack(locator, baseName, features);
+        FeatureTrack track = new FeatureTrack(locator, new FeatureCollectionSource(features));
+        track.setName(locator.getTrackName());
         track.setRendererClass(DRangerRenderer.class);
         tracks.add(track);
 
         return tracks;
     }
 
+    private int toInt(String token) {
+        try {
+            return Integer.parseInt(token);
+        }
+        catch (NumberFormatException e) {
+            return 0;
+        }
+    }
+
+
     private void setColumns(String header) throws Exception {
         String[] tokens = header.split("\t");
 
         Map<String, Integer> map = new HashMap();
         for (int i = 0; i < tokens.length; i++) {
-            map.put(tokens[i].toUpperCase(), i);
+            map.put(tokens[i].toLowerCase(), i);
         }
 
-        CHR1 = map.get("CHR1").intValue();
-        CHR2 = map.get("CHR2").intValue();
-        COUNT1 = map.get("COUNT1").intValue();
-        COUNT2 = map.get("COUNT2").intValue();
-        FILTER = map.get("FILTER").intValue();
-        MAX1 = map.get("MAX1").intValue();
-        MAX2 = map.get("MAX2").intValue();
-        MIN1 = map.get("MIN1").intValue();
-        MIN2 = map.get("MIN2").intValue();
-        STR1 = map.get("STR1").intValue();
-        STR2 = map.get("STR2").intValue();
-        TUMREADS = map.get("TUMREADS").intValue();
-
-        // Optional
-        if (map.containsKey("SITE1")) {
-            SITE1 = map.get("SITE1").intValue();
+        chr1Column = map.get("chr1").intValue();
+        str1Column = map.get("str1").intValue();
+        pos1Column = map.get("pos1").intValue();
+        chr2Column = map.get("chr2").intValue();
+        str2Column = map.get("str2").intValue();
+        pos2Column = map.get("pos2").intValue();
+
+        if (map.containsKey("num")) {
+            numColumn = map.get("num").intValue();
+        }
+        if (map.containsKey("tumreads")) {
+            tumreadsColumn = map.get("tumreads").intValue();
+        }
+        if (map.containsKey("normreads")) {
+            tumreadsColumn = map.get("normreads").intValue();
+        }
+        if (map.containsKey("class")) {
+            classColumn = map.get("class").intValue();
+        }
+        if (map.containsKey("span")) {
+            spanColumn = map.get("span").intValue();
+        }
+        if (map.containsKey("site1")) {
+            site1Column = map.get("site1").intValue();
         }
-        if (map.containsKey("SITE2")) {
-            SITE2 = map.get("SITE2").intValue();
+        if (map.containsKey("site2")) {
+            site2Column = map.get("site2").intValue();
         }
-        if (map.containsKey("GENE1")) {
-            GENE1 = map.get("GENE1").intValue();
+        if (map.containsKey("quality")) {
+            qualityColumn = map.get("quality").intValue();
         }
-        if (map.containsKey("GENE2")) {
-            GENE2 = map.get("GENE2").intValue();
+        if (map.containsKey("score")) {
+            scoreColumn = map.get("score").intValue();
         }
     }
 }
diff --git a/src/org/broad/igv/feature/dranger/DRangerRenderer.java b/src/org/broad/igv/feature/dranger/DRangerRenderer.java
index a5077ba..663ad4c 100644
--- a/src/org/broad/igv/feature/dranger/DRangerRenderer.java
+++ b/src/org/broad/igv/feature/dranger/DRangerRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,11 +22,10 @@
  */
 package org.broad.igv.feature.dranger;
 
-import cern.colt.map.OpenIntObjectHashMap;
 import org.broad.igv.feature.Feature;
 import org.broad.igv.renderer.FeatureRenderer;
+import org.broad.igv.track.FeatureTrack;
 import org.broad.igv.track.RenderContext;
-import org.broad.igv.track.Track;
 
 import java.awt.*;
 import java.util.List;
@@ -36,12 +35,10 @@ import java.util.List;
  */
 public class DRangerRenderer extends FeatureRenderer {
 
-    static OpenIntObjectHashMap greyscaleCache = new OpenIntObjectHashMap(500);
-
     //TODO -- this is a copy from MutationRenderer.  Refactor to move this up
     // the class hierarchy.
     public void renderFeatures(List<Feature> featureList, RenderContext context,
-                               Rectangle trackRectangle, Track track) {
+                               Rectangle trackRectangle, FeatureTrack track) {
 
         double origin = context.getOrigin();
         double locScale = context.getScale();
@@ -80,23 +77,11 @@ public class DRangerRenderer extends FeatureRenderer {
         int w = (int) Math.ceil(length / locScale);
         int y = (int) Math.max(rect.getY(), (rect.getY() + (rect.getHeight() - h) / 2));
 
-        Graphics2D g = context.getGraphic2DForColor(getFeatureColor(dr));
+        Graphics2D g = context.getGraphic2DForColor(feature.getColor());
         g.fillRect(x, y, w, h);
 
 
     }
 
-    private Color getFeatureColor(DRangerFeature feature) {
-        // Set color used to draw the feature
 
-        //TODO -- experimenting with this for DRanger only.  Remove instanceOf if it works out
-        int shade = (int) (226 - (226.0 / 834.0) * (feature.getScore()));
-        shade = Math.max(0, Math.min(226, shade));
-        Color color = (Color) greyscaleCache.get(shade);
-        if (color == null) {
-            color = new Color(shade, shade, shade);
-            greyscaleCache.put(shade, color);
-        }
-        return color;
-    }
 }
diff --git a/src/org/broad/igv/h5/BulkPropertyUpdater.java b/src/org/broad/igv/h5/BulkPropertyUpdater.java
deleted file mode 100644
index 5b4534a..0000000
--- a/src/org/broad/igv/h5/BulkPropertyUpdater.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-
-package org.broad.igv.h5;
-
-//~--- JDK imports ------------------------------------------------------------
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.util.HashMap;
-import java.util.Map;
-
-
-/**
- * @author jrobinso
- */
-public class BulkPropertyUpdater {
-
-    /**
-     * Method description
-     *
-     * @param args
-     */
-    public static void main(String[] args) {
-        try {
-
-            // String h5file = "/Users/jrobinso/IGV/TestData/K562Sequence.h5";
-            HashMap properties = new HashMap();
-            properties.put("track.autoscale", "false");
-            properties.put("track.minValue", 0.0);
-            properties.put("track.maxValue", 20.0);
-
-            String dir = "/Volumes/xchip_tcga_scratch/igv/epigenetics/hg18";
-            for (File f : (new File(dir)).listFiles()) {
-                String path = f.getAbsolutePath();
-                if (path.endsWith("h5")) {
-                    System.out.println(path);
-                    updateFile(path, properties);
-                }
-            }
-        }
-        catch (FileNotFoundException ex) {
-            ex.printStackTrace();
-        }
-    }
-
-    static void updateFile(String filename, Map<String, String> properties)
-            throws FileNotFoundException {
-
-        HDF5LocalWriter writer = new HDF5LocalWriter();
-
-        int fileId = writer.openFile(filename);
-
-        int rootGroupId = writer.openGroup(fileId, "/");
-
-        for (String key : properties.keySet()) {
-            Object value = properties.get(key);
-
-            try {
-                writer.deleteAttribute(rootGroupId, key);
-            }
-            catch (DataAccessException e) {
-
-                // This is normal if the attribute did not exist.  Ignore.
-            }
-
-            writer.writeAttribute(rootGroupId, key, value);
-
-        }
-
-        writer.closeGroup(rootGroupId);
-
-        writer.closeFile(fileId);
-    }
-
-}
diff --git a/src/org/broad/igv/h5/DataAccessException.java b/src/org/broad/igv/h5/DataAccessException.java
index 39288e6..9ab594c 100644
--- a/src/org/broad/igv/h5/DataAccessException.java
+++ b/src/org/broad/igv/h5/DataAccessException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/h5/H5BinBase.java b/src/org/broad/igv/h5/H5BinBase.java
deleted file mode 100644
index 09d7878..0000000
--- a/src/org/broad/igv/h5/H5BinBase.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.h5;
-
-import java.io.PrintStream;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * @author jrobinso
- */
-public class H5BinBase {
-
-    enum DataType {
-
-        BYTE, SHORT, INT, FLOAT, DOUBLE, STRING
-    }
-
-    ;
-    /**
-     * Index of chr -> datasetName -> position
-     */
-    protected Map<String, Long> groupIndex;
-    protected Map<String, Integer> entityIdMap;
-    protected Map<Integer, Group> groupCache;
-    protected Map<Integer, Dataset> datasetCache;
-
-    public static class Entity {
-
-        String fullName;
-        Map<String, String> attributes = new HashMap();
-    }
-
-    public static class Group extends Entity {
-
-        Group(String fullName) {
-            this.fullName = fullName;
-        }
-
-        boolean isEmpty() {
-            return attributes.isEmpty();
-        }
-
-        void dump(PrintStream out) {
-            out.println(fullName);
-            for (Map.Entry<String, String> entry : attributes.entrySet()) {
-                out.println("\t" + entry.getKey() + " = " + entry.getValue());
-            }
-        }
-    }
-
-    public static class Dataset extends Entity {
-        int nRows;
-        int nColumns;
-        DataType type;
-        long[] rowPositions;
-
-        Dataset(String fullName, int nRows, int nColumns, DataType type) {
-            this.fullName = fullName;
-            this.type = type;
-            this.nRows = nRows;
-            this.nColumns = nColumns;
-            rowPositions = new long[nRows];
-        }
-
-        void dump(PrintStream out) {
-            out.println(fullName + "  [" + nRows + "," + nColumns + "]");
-            for (Map.Entry<String, String> entry : attributes.entrySet()) {
-                out.println("\t" + entry.getKey() + " = " + entry.getValue());
-            }
-        }
-    }
-
-    public H5BinBase() {
-        entityIdMap = new HashMap();
-        groupCache = new HashMap();
-        datasetCache = new HashMap();
-
-    }
-
-    protected String getFullName(int locId, String name) {
-
-        // Names that start with / are absolute already
-        if (name.startsWith("/")) {
-            return name;
-        }
-
-        String entityName = groupCache.get(locId).fullName;
-        if (!entityName.endsWith("/")) {
-            entityName = entityName + "/";
-        }
-        String fullName = entityName + name;
-        return fullName;
-    }
-
-    protected Group getParentGroup(int locId, String name) {
-        String fullPath = getFullName(locId, name);
-        int idx = fullPath.lastIndexOf("/");
-        String parentName = fullPath.substring(0, idx);
-        if (!entityIdMap.containsKey(idx)) {
-            throw new java.lang.IllegalArgumentException("No parent group found for: " + fullPath);
-        } else {
-            int parentId = entityIdMap.get(parentName);
-            return groupCache.get(parentId);
-        }
-
-    }
-
-    protected void addEntity(Integer id, String name) {
-        groupCache.put(id, new Group(name));
-        entityIdMap.put(name, id);
-    }
-
-    protected int getEntityId(String name) {
-        if (!entityIdMap.containsKey(name)) {
-            System.out.println();
-        }
-        return entityIdMap.get(name);
-    }
-
-    protected Group getEntity(int id) {
-        return groupCache.get(id);
-    }
-
-    protected String getEntityName(int id) {
-        return groupCache.get(id).fullName;
-    }
-
-    protected void removeEntity(int id) {
-        Group entity = groupCache.get(id);
-        groupCache.remove(id);
-        entityIdMap.remove(entity.fullName);
-    }
-}
diff --git a/src/org/broad/igv/h5/H5BinReader.java b/src/org/broad/igv/h5/H5BinReader.java
deleted file mode 100644
index f612262..0000000
--- a/src/org/broad/igv/h5/H5BinReader.java
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.h5;
-
-import org.apache.log4j.Logger;
-
-import java.io.*;
-import java.nio.channels.FileChannel;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/**
- * @author jrobinso
- */
-public class H5BinReader extends H5BinBase implements HDF5Reader {
-
-    static final Logger log = Logger.getLogger(H5BinReader.class);
-    File file;
-    //DataInputStream dis = null;
-    FileInputStream fis = null;
-    FileChannel channel = null;
-    Map<String, Long> datasetIndex;
-
-    public H5BinReader(File file) {
-        this.file = file;
-
-        try {
-            int fileId = file.getAbsolutePath().hashCode();
-            addEntity(fileId, "/");
-
-            fis = new FileInputStream(file);
-            channel = fis.getChannel();
-
-            int version = readInt();
-            readGroups();
-
-        } catch (IOException ex) {
-            log.error("Error creating file: " + file, ex);
-            throw new RuntimeException("Error creating file: " + file, ex);
-        }
-
-    }
-
-    /**
-     * Read groups and database indexes
-     */
-    private synchronized void readGroups() throws IOException {
-
-        long lastPosition = file.length() - Long.SIZE / 8;
-        channel.position(lastPosition);
-        long magicNumber = readLong();
-        channel.position(magicNumber);
-
-        int nBytes = (int) (lastPosition - magicNumber);
-        byte[] bytes = new byte[nBytes];
-        readFully(bytes);
-
-        DataInputStream bdis = new DataInputStream(new ByteArrayInputStream(bytes));
-
-        // Read dataset index
-        int nDatasets = bdis.readInt();
-        datasetIndex = new LinkedHashMap(nDatasets);
-        for (int i = 0; i < nDatasets; i++) {
-            String name = bdis.readUTF();
-            long fPosition = bdis.readLong();
-            datasetIndex.put(name, fPosition);
-        }
-
-        // read groups
-        int nGroups = bdis.readInt();
-        groupCache = new HashMap(nGroups);
-        for (int i = 0; i < nGroups; i++) {
-            String name = bdis.readUTF();
-            int id = name.hashCode();
-            Group g = new Group(name);
-            this.groupCache.put(id, g);
-
-            int nAttributes = bdis.readInt();
-
-            g.attributes = new HashMap(nAttributes);
-            for (int j = 0; j < nAttributes; j++) {
-                String key = bdis.readUTF();
-                String value = bdis.readUTF();
-                g.attributes.put(key, value);
-            }
-
-            // TODO -- put group in cache
-
-        }
-    }
-
-    private synchronized Dataset readDataset() throws IOException {
-
-        int nBytes = readInt();
-        byte[] buffer = new byte[nBytes];
-        readFully(buffer);
-
-        DataInputStream dis = new DataInputStream(new ByteArrayInputStream(buffer));
-        String fullName = dis.readUTF();
-        String typeString = dis.readUTF();
-        DataType type = DataType.valueOf(typeString);
-        int nRows = dis.readInt();
-        int nCols = dis.readInt();
-        Dataset dataset = new Dataset(fullName, nRows, nCols, type);
-
-        int nAttrs = dis.readInt();
-        if (nAttrs > 0) {
-        }
-
-        dataset.rowPositions = new long[nRows];
-
-
-        for (int i = 0; i < nRows; i++) {
-            dataset.rowPositions[i] = dis.readLong();
-        }
-        return dataset;
-
-    }
-
-    public void closeFile() {
-        try {
-            fis.close();
-        } catch (IOException ex) {
-            log.error("Error closing file: " + file, ex);
-        }
-    }
-
-    public synchronized int openDataset(String datasetName) {
-        try {
-            if (entityIdMap.containsKey(datasetName)) {
-                return entityIdMap.get(datasetName);
-            } else {
-                int id = datasetName.hashCode();
-                entityIdMap.put(datasetName, id);
-                long dsFilePosition = datasetIndex.get(datasetName);
-
-                channel.position(dsFilePosition);
-                Dataset dataset = readDataset();
-                datasetCache.put(id, dataset);
-
-                return id;
-            }
-        } catch (IOException ex) {
-            log.error("Error opening dataset: " + datasetName, ex);
-            throw new RuntimeException("Error opening dataset: " + datasetName, ex);
-        }
-    }
-
-    public void closeDataset(int datasetId) {
-        // No-op
-    }
-
-    public int openGroup(String groupPath) {
-        int id = groupPath.hashCode();
-        entityIdMap.put(groupPath, id);
-        return id;
-    }
-
-    public void closeGroup(int groupId) {
-        // no op
-    }
-
-    public int readIntegerAttribute(int groupId, String attrName) throws ObjectNotFoundException {
-        String value = readStringAttribute(groupId, attrName);
-        return Integer.parseInt(value);
-    }
-
-    public String readStringAttribute(int groupId, String attrName) {
-        Group group = groupCache.get(groupId);
-        if (group == null) {
-            throw new ObjectNotFoundException("Group not found");
-        }
-        String value = group.attributes.get(attrName);
-        if (value == null) {
-            throw new ObjectNotFoundException("Attribute not found: " + attrName);
-        }
-        return value;
-    }
-
-    public double readDoubleAttribute(int groupId, String attrName) {
-        String value = readStringAttribute(groupId, attrName);
-        return Double.parseDouble(value);
-    }
-
-    /**
-     * Read all values from the dataset as an array of floats.  Note that this method implicitly
-     * assumes a 1-d dataset.
-     *
-     * @param datasetId
-     * @return
-     */
-    public synchronized float[] readAllFloats(int datasetId) {
-        try {
-            Dataset ds = datasetCache.get(datasetId);
-            assert (ds.nRows == 1);
-
-            int nBytes = ds.nColumns * Float.SIZE / 8;
-            byte[] buffer = new byte[nBytes];
-            readFully(buffer);
-            DataInputStream dis = new DataInputStream(new ByteArrayInputStream(buffer));
-
-            float[] data = new float[ds.nColumns];
-            channel.position(ds.rowPositions[0]);
-            for (int i = 0; i < ds.nColumns; i++) {
-                data[i] = dis.readFloat();
-            }
-            return data;
-        } catch (IOException ex) {
-            log.error("Error reading datset", ex);
-            throw new RuntimeException("Error reading datset", ex);
-        }
-    }
-
-    public synchronized int[] readAllInts(int datasetId) {
-        try {
-            Dataset ds = datasetCache.get(datasetId);
-            assert (ds.nRows == 1);
-            int[] data = new int[ds.nColumns];
-            channel.position(ds.rowPositions[0]);
-
-            int nBytes = ds.nColumns * Integer.SIZE / 8;
-            byte[] buffer = new byte[nBytes];
-            readFully(buffer);
-            DataInputStream dis = new DataInputStream(new ByteArrayInputStream(buffer));
-
-            for (int i = 0; i < ds.nColumns; i++) {
-                data[i] = dis.readInt();
-            }
-            return data;
-        } catch (IOException ex) {
-            log.error("Error reading datset", ex);
-            throw new RuntimeException("Error reading datset", ex);
-        }
-    }
-
-    public synchronized String[] readAllStrings(int datasetId) {
-        try {
-            Dataset ds = datasetCache.get(datasetId);
-            assert (ds.nRows == 1);
-            String[] data = new String[ds.nColumns];
-            channel.position(ds.rowPositions[0]);
-
-            int nBytes = readInt();
-            byte[] buffer = new byte[nBytes];
-            readFully(buffer);
-            DataInputStream dis = new DataInputStream(new ByteArrayInputStream(buffer));
-
-            for (int i = 0; i < ds.nColumns; i++) {
-                data[i] = dis.readUTF();
-            }
-            return data;
-        } catch (IOException ex) {
-            log.error("Error reading datset", ex);
-            throw new RuntimeException("Error reading datset", ex);
-        }
-    }
-
-    public synchronized float[] readFloats(int datasetId, int fromIndex, int toIndex) {
-        try {
-            Dataset ds = datasetCache.get(datasetId);
-            assert (ds.nRows == 1);
-            assert (fromIndex >= 0);
-            assert (toIndex <= ds.nColumns);
-
-            int nPts = toIndex - fromIndex;
-            float[] data = new float[nPts];
-            long offset = fromIndex * Float.SIZE / 8;
-            channel.position(ds.rowPositions[0] + offset);
-
-            int nBytes = nPts * Float.SIZE / 8;
-            byte[] buffer = new byte[nBytes];
-            readFully(buffer);
-            DataInputStream dis = new DataInputStream(new ByteArrayInputStream(buffer));
-
-
-            for (int i = 0; i < nPts; i++) {
-                data[i] = dis.readFloat();
-            }
-            return data;
-        } catch (IOException ex) {
-            log.error("Error reading datset", ex);
-            throw new RuntimeException("Error reading datset", ex);
-        }
-    }
-
-    public synchronized int[] readInts(int datasetId, int fromIndex, int toIndex) {
-        try {
-
-            Dataset ds = datasetCache.get(datasetId);
-            assert (ds.nRows == 1);
-            assert (fromIndex >= 0);
-            assert (toIndex <= ds.nColumns);
-
-            int nPts = toIndex - fromIndex;
-            int[] data = new int[nPts];
-            long offset = fromIndex * Integer.SIZE / 8;
-            channel.position(ds.rowPositions[0] + offset);
-
-            int nBytes = nPts * Integer.SIZE / 8;
-            byte[] buffer = new byte[nBytes];
-            readFully(buffer);
-            DataInputStream dis = new DataInputStream(new ByteArrayInputStream(buffer));
-
-            for (int i = 0; i < nPts; i++) {
-                data[i] = dis.readInt();
-            }
-            return data;
-        } catch (IOException ex) {
-            log.error("Error reading datset", ex);
-            throw new RuntimeException("Error reading datset", ex);
-        }
-    }
-
-    public synchronized float[][] readDataSlice(int datasetId, int fromIndex, int toIndex) {
-        try {
-            Dataset ds = datasetCache.get(datasetId);
-            assert (fromIndex >= 0);
-            assert (toIndex <= ds.nColumns);
-
-            int nPts = toIndex - fromIndex;
-            int nBytes = nPts * Float.SIZE / 8;
-            byte[] buffer = new byte[nBytes];
-            long offset = fromIndex * Float.SIZE / 8;
-
-            float[][] data = new float[ds.nRows][nPts];
-            for (int row = 0; row < ds.nRows; row++) {
-                channel.position(ds.rowPositions[row] + offset);
-
-                readFully(buffer);
-                DataInputStream dis = new DataInputStream(new ByteArrayInputStream(buffer));
-
-                for (int i = 0; i < nPts; i++) {
-                    data[row][i] = dis.readFloat();
-                }
-            }
-            return data;
-        } catch (IOException ex) {
-            log.error("Error reading datset", ex);
-            throw new RuntimeException("Error reading datset", ex);
-        }
-    }
-
-    /**
-     * Bytes for this operation are read from the contained
-     * input stream.
-     *
-     * @param b the buffer into which the data is read.
-     * @throws EOFException if this input stream reaches the end before
-     *                      reading all the bytes.
-     * @throws IOException  if an I/O error occurs.
-     */
-    private void readFully(byte b[]) throws IOException {
-        int off = 0;
-        int len = b.length;
-        if (len < 0) {
-            throw new IndexOutOfBoundsException();
-        }
-        int n = 0;
-        while (n < len) {
-            int count = fis.read(b, off + n, len - n);
-            if (count < 0) {
-                throw new EOFException();
-            }
-            n += count;
-        }
-    }
-
-    private int readInt() throws IOException {
-        int ch1 = fis.read();
-        int ch2 = fis.read();
-        int ch3 = fis.read();
-        int ch4 = fis.read();
-        if ((ch1 | ch2 | ch3 | ch4) < 0) {
-            throw new EOFException();
-        }
-        return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
-    }
-
-    private long readLong() throws IOException {
-        byte[] readBuffer = new byte[8];
-        readFully(readBuffer);
-        return (((long) readBuffer[0] << 56) +
-                ((long) (readBuffer[1] & 255) << 48) +
-                ((long) (readBuffer[2] & 255) << 40) +
-                ((long) (readBuffer[3] & 255) << 32) +
-                ((long) (readBuffer[4] & 255) << 24) +
-                ((readBuffer[5] & 255) << 16) +
-                ((readBuffer[6] & 255) << 8) +
-                ((readBuffer[7] & 255) << 0));
-    }
-}
diff --git a/src/org/broad/igv/h5/H5BinUtils.java b/src/org/broad/igv/h5/H5BinUtils.java
deleted file mode 100644
index 7353e12..0000000
--- a/src/org/broad/igv/h5/H5BinUtils.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-package org.broad.igv.h5;
-
-import java.io.File;
-import java.util.Map;
-
-/**
- * @author jrobinso
- */
-public class H5BinUtils {
-
-
-    public static void main(String[] args) {
-        //String f = "/Users/jrobinso/IGV/TestData/MIP_44.cn.hbin";
-        String f = "/Users/jrobinso/IGV/projects/preprocessors/ADSCt1.H3K27me3.aligned.hbin";
-        dumpEntities(f);
-    }
-
-    public static void dumpEntities(String binFile) {
-
-        H5BinReader reader = new H5BinReader(new File(binFile));
-
-        Map<Integer, H5BinBase.Group> groups = reader.groupCache;
-        for (H5BinBase.Group group : groups.values()) {
-            group.dump(System.out);
-        }
-
-        Map<String, Long> dbIndex = reader.datasetIndex;
-        for (String dsname : dbIndex.keySet()) {
-            reader.openDataset(dsname);
-        }
-        for (H5BinBase.Dataset ds : reader.datasetCache.values()) {
-            ds.dump(System.out);
-        }
-
-    }
-
-}
diff --git a/src/org/broad/igv/h5/H5Constants.java b/src/org/broad/igv/h5/H5Constants.java
deleted file mode 100644
index 0ad574e..0000000
--- a/src/org/broad/igv/h5/H5Constants.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-package org.broad.igv.h5;
-
-import ncsa.hdf.hdf5lib.HDF5Constants;
-
-/**
- * @author jrobinso
- */
-public class H5Constants {
-
-    public static final int NATIVE_FLOAT = HDF5Constants.H5T_NATIVE_FLOAT;
-    public static final int NATIVE_DOUBLE = HDF5Constants.H5T_NATIVE_DOUBLE;
-    public static final int NATIVE_INT = HDF5Constants.H5T_NATIVE_INT32;
-    public static final int NATIVE_SHORT = HDF5Constants.H5T_NATIVE_INT16;
-    public static final int STRING = HDF5Constants.H5T_C_S1;
-}
diff --git a/src/org/broad/igv/h5/HDF5EntityCache.java b/src/org/broad/igv/h5/HDF5EntityCache.java
deleted file mode 100644
index 460db3a..0000000
--- a/src/org/broad/igv/h5/HDF5EntityCache.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.h5;
-
-import ncsa.hdf.hdf5lib.H5;
-import ncsa.hdf.hdf5lib.exceptions.HDF5LibraryException;
-import ncsa.hdf.hdf5lib.exceptions.HDF5SymbolTableException;
-import org.apache.log4j.Logger;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * @author jrobinso
- */
-public class HDF5EntityCache {
-
-    private Logger log = Logger.getLogger(HDF5EntityCache.class);
-
-    int fileId;
-
-    Map<String, Integer> datasetCache;
-
-    Map<String, Integer> groupCache;
-
-    public HDF5EntityCache(int fileId) {
-        this.fileId = fileId;
-        datasetCache = new HashMap();
-        groupCache = new HashMap();
-    }
-
-    public synchronized void closeAllEntities() {
-        for (Integer dsId : datasetCache.values()) {
-            closeDataset(dsId);
-        }
-
-        datasetCache.clear();
-
-    }
-
-    public int openDataset(String dsName) {
-        try {
-            Integer dsId = datasetCache.get(dsName);
-            if (dsId == null) {
-                dsId = H5.H5Dopen(fileId, dsName);
-                datasetCache.put(dsName, dsId);
-            }
-            return dsId;
-        } catch (HDF5SymbolTableException ex) {
-            throw new ObjectNotFoundException("Error opening dataset: " + dsName);
-        } catch (HDF5LibraryException ex) {
-            log.error("Error opening dataset", ex);
-            throw new RuntimeException(ex);
-        }
-
-    }
-
-    /**
-     * Close an hdf5 dataset
-     */
-    private void closeDataset(int datasetId) {
-
-        try {
-            H5.H5Dclose(datasetId);
-        } catch (HDF5LibraryException ex) {
-            log.error("Error closing dataset", ex);
-            throw new RuntimeException("Error closing dataset");
-        }
-
-    }
-
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            closeAllEntities();
-        } finally {
-            super.finalize();
-        }
-    }
-    /*
-    static class DatasetReference {
-    int datasetId;
-    DatasetReference(int datasetId) {
-    datasetId = datasetId;
-    }
-    }
-     * */
-}
diff --git a/src/org/broad/igv/h5/HDF5LocalReader.java b/src/org/broad/igv/h5/HDF5LocalReader.java
deleted file mode 100644
index 4eb27c7..0000000
--- a/src/org/broad/igv/h5/HDF5LocalReader.java
+++ /dev/null
@@ -1,797 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-
-/*
-* IGVTestReadH5Lib.java
-*
-* Tests reading an IGV H5 file using the low level C wrapper library.
-* This file maps java functions as directly as possible to the underlying
-* C functions.  It is much faster than the object API.
-*/
-package org.broad.igv.h5;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import ncsa.hdf.hdf5lib.H5;
-import ncsa.hdf.hdf5lib.HDF5Constants;
-import ncsa.hdf.hdf5lib.exceptions.HDF5AttributeException;
-import ncsa.hdf.hdf5lib.exceptions.HDF5Exception;
-import ncsa.hdf.hdf5lib.exceptions.HDF5LibraryException;
-import org.apache.log4j.Logger;
-import org.broad.igv.util.ObjectCache;
-
-import java.io.FileNotFoundException;
-
-/**
- * @author jrobinso
- */
-public class HDF5LocalReader implements HDF5Reader {
-
-    private static Logger log = Logger.getLogger(HDF5LocalReader.class);
-
-    /**
-     * Field description
-     */
-    public static int RDONLY = ncsa.hdf.hdf5lib.HDF5Constants.H5F_ACC_RDONLY;
-
-    static ObjectCache<Integer, HDF5EntityCache> entityCaches = new ObjectCache();
-
-    private int fileId = -1;
-
-    /**
-     * Constructs ...
-     *
-     * @param path
-     */
-    public HDF5LocalReader(String path) {
-        try {
-            this.fileId = openFile(path, RDONLY);
-        }
-        catch (FileNotFoundException ex) {
-            ex.printStackTrace();
-        }
-    }
-
-    /**
-     * Open an HDF 5 and return a hnadle to the file.  The handle
-     * should be stored by the caller and used for further
-     * file operaions.
-     *
-     * @param path a unique identifier for the file.  Will be the file path for a
-     *             local reader, but could be something else for a remote reader.
-     * @return handle to the file (basically a C pointer)
-     * @throws java.io.FileNotFoundException
-     */
-    private int openFile(String path, int mode) throws FileNotFoundException {
-        try {
-            fileId = H5.H5Fopen(path, mode, ncsa.hdf.hdf5lib.HDF5Constants.H5P_DEFAULT);
-            entityCaches.put(fileId, new HDF5EntityCache(fileId));
-            return fileId;
-        }
-        catch (HDF5LibraryException ex) {
-            String msg = "Error opening HDF5 file: " + path;
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-    }
-
-    /**
-     * Close an hdf5 file
-     */
-    public void closeFile() {
-        try {
-            if (fileId > 0) {
-                H5.H5Fclose(fileId);
-                HDF5EntityCache cache = entityCaches.get(fileId);
-                if (cache != null) {
-                    cache.closeAllEntities();
-                }
-                fileId = -1;
-            }
-        }
-        catch (HDF5LibraryException ex) {
-            String msg = "Error closing HDF5 file";
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-    }
-
-    /**
-     * Method description
-     *
-     * @param dsName
-     * @return
-     */
-    public int openDataset(String dsName) {
-        HDF5EntityCache cache = entityCaches.get(fileId);
-        if (cache == null) {
-            cache = new HDF5EntityCache(fileId);
-            entityCaches.put(fileId, cache);
-        }
-        return cache.openDataset(dsName);
-    }
-
-    /**
-     * Close an hdf5 dataset
-     *
-     * @param datasetId
-     */
-    public void closeDataset(int datasetId) {
-
-        // Closing now managed by cache
-    }
-
-    /**
-     * Method description
-     *
-     * @param name
-     * @return
-     */
-    public int openGroup(String name) {
-        try {
-            return H5.H5Gopen(fileId, name);
-        }
-        catch (ncsa.hdf.hdf5lib.exceptions.HDF5SymbolTableException ex) {
-            String msg = "Error opening group: " + name;
-            if (log.isDebugEnabled()) {
-                log.debug(msg, ex);
-            }
-            throw new ObjectNotFoundException(msg);
-        }
-        catch (HDF5LibraryException ex) {
-            String msg = "Error opening group: " + name;
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Close an hdf5 group
-     *
-     * @param groupId
-     */
-    public void closeGroup(int groupId) {
-        try {
-            H5.H5Gclose(groupId);
-        }
-        catch (HDF5LibraryException ex) {
-            String msg = "Error closing HDF5 group";
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-    }
-
-    /**
-     * Returns the HDF typeId and array length for an array.  Info is returned as
-     * a 2 element int array.  Yes, a class could be used but seems excessive
-     * for this private method.
-     */
-    private int getTypeForArray(Object array) {
-
-        if ((array instanceof Integer) || (array instanceof int[]) || (array instanceof int[][])) {
-            return HDF5Constants.H5T_NATIVE_INT;
-        }
-
-        if ((array instanceof Long) || (array instanceof long[]) || (array instanceof long[][])) {
-            return HDF5Constants.H5T_NATIVE_LLONG;
-        }
-
-        if ((array instanceof Float) || (array instanceof float[]) || (array instanceof float[][])) {
-            return HDF5Constants.H5T_NATIVE_FLOAT;
-        }
-
-        if ((array instanceof Number) || (array instanceof double[])
-                || (array instanceof double[][])) {
-            return HDF5Constants.H5T_NATIVE_DOUBLE;
-        }
-
-        if (array instanceof char[]) {
-            return createStringDatatype(1);
-        }
-
-        String msg = "Error: No HDF Type for: " + array.getClass().getName();
-        log.error(msg);
-        throw new DataAccessException("No HDF Type for: " + array.getClass().getName());
-
-    }
-
-    /**
-     * Method description
-     *
-     * @param strLength
-     * @return
-     */
-    public int createStringDatatype(int strLength) {
-        try {
-            int tid = H5.H5Tcopy(HDF5Constants.H5T_C_S1);
-            H5.H5Tset_size(tid, strLength);
-            return tid;
-        }
-        catch (HDF5LibraryException ex) {
-            String msg = "Error creating string datatype. Length = " + strLength;
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Read an interval from a single data row from the 2-D array.
-     *
-     * @param datasetId
-     * @param rowId     The row number (first row is zero)
-     * @param fromIndex Start column index
-     * @param toIndex   End column index
-     * @return
-     */
-    public float[] readDataRowSlice(int datasetId, int rowId, int fromIndex, int toIndex) {
-        try {
-            int dataspace = getDataspace(datasetId);
-            int nPts = toIndex - fromIndex + 1;
-
-            long[] offset = new long[2];
-            long[] count = new long[2];
-            offset[0] = rowId;
-            offset[1] = fromIndex;
-            count[0] = 1;
-            count[1] = nPts;
-            int status1 = H5.H5Sselect_hyperslab(dataspace, HDF5Constants.H5S_SELECT_SET, offset,
-                    null, count, null);
-
-            // Step 2.   Create a memory dataspace
-            long[] memDims = new long[1];
-            memDims[0] = nPts;
-            int memspace = H5.H5Screate_simple(1, memDims, null);
-
-            // Step 4.  Read the data
-            float[] partialData = new float[nPts];
-            H5.H5Dread(datasetId, HDF5Constants.H5T_NATIVE_FLOAT, memspace, dataspace,
-                    HDF5Constants.H5P_DEFAULT, partialData);
-
-            return partialData;
-
-        }
-        catch (HDF5Exception ex) {
-            String msg = "Error reading data row";
-            log.error(msg);
-            throw new DataAccessException(msg);
-        }
-
-    }
-
-    /**
-     * Read a slice defined by fromIndex and toIndex from a 2-D array
-     *
-     * @param datasetId
-     * @param fromIndex The start column index
-     * @param toIndex   The end column index
-     * @return
-     */
-    public float[][] readDataSlice(int datasetId, int fromIndex, int toIndex) {
-        try {
-            int dataspace = getDataspace(datasetId);
-            int nPts = toIndex - fromIndex + 1;
-
-            long[] dims = getDims(datasetId);
-            int nRows = (int) dims[0];
-
-            long[] offset = new long[2];
-            long[] count = new long[2];
-            offset[0] = 0;
-            offset[1] = fromIndex;
-            count[0] = nRows;
-            count[1] = nPts;
-            int status1 = H5.H5Sselect_hyperslab(dataspace, HDF5Constants.H5S_SELECT_SET, offset,
-                    null, count, null);
-
-            // Step 2.   Create a memory dataspace
-            long[] memDims = new long[2];
-            memDims[0] = nRows;
-            memDims[1] = nPts;
-            int memspace = H5.H5Screate_simple(2, memDims, null);
-
-            // Step 4.  Read the data
-            float[][] partialData = new float[nRows][nPts];
-            H5.H5Dread(datasetId, HDF5Constants.H5T_NATIVE_FLOAT, memspace, dataspace,
-                    HDF5Constants.H5P_DEFAULT, partialData);
-
-            return partialData;
-
-        }
-        catch (HDF5Exception ex) {
-            String msg = "Error reading data";
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * @param groupId
-     * @param name
-     * @return
-     */
-    public double readDoubleAttribute(int groupId, String name) {
-
-        try {
-            int attrId = H5.H5Aopen_name(groupId, name);
-            double[] buffer = new double[1];
-            H5.H5Aread(attrId, HDF5Constants.H5T_NATIVE_DOUBLE, buffer);
-            H5.H5Aclose(attrId);
-            return buffer[0];
-        }
-        catch (HDF5AttributeException e) {
-            throw new ObjectNotFoundException("Attribute not found: " + name);
-        }
-        catch (HDF5Exception ex) {
-            String msg = "Error reading attribute from dataset";
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * @param groupId
-     * @param name
-     * @return
-     * @throws ObjectNotFoundException
-     */
-    public int readIntegerAttribute(int groupId, String name) throws ObjectNotFoundException {
-
-
-        try {
-            int attrId = H5.H5Aopen_name(groupId, name);
-            int[] buffer = new int[1];
-            H5.H5Aread(attrId, HDF5Constants.H5T_NATIVE_INT, buffer);
-            H5.H5Aclose(attrId);
-            return buffer[0];
-        }
-        catch (HDF5AttributeException e) {
-            throw new ObjectNotFoundException("Attribute not found: " + name);
-        }
-        catch (HDF5Exception ex) {
-            String msg = "Error reading attribute from dataset";
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Method description
-     *
-     * @param groupId
-     * @param name
-     * @return
-     */
-    public long readLongAttribute(int groupId, String name) {
-
-        try {
-            int attrId = H5.H5Aopen_name(groupId, name);
-            long[] buffer = new long[1];
-            H5.H5Aread(attrId, HDF5Constants.H5T_NATIVE_LONG, buffer);
-            H5.H5Aclose(attrId);
-            return buffer[0];
-        }
-        catch (HDF5AttributeException e) {
-            throw new ObjectNotFoundException("Attribute not found: " + name);
-        }
-        catch (HDF5Exception ex) {
-            String msg = "Error reading attribute from dataset";
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Method description
-     *
-     * @param groupId
-     * @param name
-     * @return
-     */
-    public String readStringAttribute(int groupId, String name) {
-
-        try {
-            int attrId = H5.H5Aopen_name(groupId, name);
-            int type = H5.H5Aget_type(attrId);
-            int size = H5.H5Tget_size(type);
-            byte[] buffer = new byte[2 * size];
-            H5.H5Aread(attrId, type, buffer);
-            H5.H5Aclose(attrId);
-            return new String(buffer).trim();
-        }
-        catch (HDF5AttributeException e) {
-            throw new ObjectNotFoundException("Attribute not found: " + name);
-        }
-        catch (HDF5Exception ex) {
-            String msg = "Error reading attribute from dataset";
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Method description
-     *
-     * @param attId
-     */
-    public void closeAttribute(int attId) {
-        try {
-            H5.H5Aclose(attId);
-        }
-        catch (HDF5LibraryException ex) {
-            String msg = "Error closing attribute";
-            log.error(msg);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Return the dimensions of the dataset.
-     */
-    private long[] getDims(int datasetId) {
-        try {
-            int dataspace = H5.H5Dget_space(datasetId);
-            int nDims = H5.H5Sget_simple_extent_ndims(dataspace);
-            long[] dims = new long[nDims];
-            long[] maxDims = new long[nDims];
-            H5.H5Sget_simple_extent_dims(dataspace, dims, maxDims);
-            return dims;
-        }
-        catch (HDF5LibraryException ex) {
-            String msg = "Error getting dataset dimensions";
-            log.error(msg);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Return the dataspace for the dataset
-     */
-    private int getDataspace(int datasetId) {
-        try {
-            return H5.H5Dget_space(datasetId);
-        }
-        catch (HDF5LibraryException ex) {
-            String msg = "Error getting dataspace";
-            log.error(msg);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Read the entire dataset as an array of strings.
-     * for (int i=0; i<rank; i++)
-     *
-     * @param datasetId
-     * @return
-     */
-    public String[] readAllStrings(int datasetId) {
-        try {
-            long[] dims = getDims(datasetId);
-            int typeId = H5.H5Dget_type(datasetId);
-
-
-            int numberOfStrings = 1;
-            for (int i = 0; i < dims.length; i++) {
-                numberOfStrings *= (int) dims[i];
-            }
-
-//          figure out the string size and number of strings
-            int stringLength = (int) H5.H5Tget_size(typeId);
-            int bufferSize = numberOfStrings * stringLength;
-            byte[] byteBuff = new byte[bufferSize];
-
-            // read the string data into byte buff
-            int mspace = HDF5Constants.H5S_ALL;
-            int fspace = HDF5Constants.H5S_ALL;
-            int plist = HDF5Constants.H5P_DEFAULT;
-            H5.H5Dread(datasetId, typeId, mspace, fspace, plist, byteBuff);
-
-            // convert byte array into string array
-            String[] strOut = new String[numberOfStrings];
-            for (int i = 0; i < numberOfStrings; i++) {
-                strOut[i] = new String(byteBuff, i * stringLength, stringLength).trim();
-            }
-
-            return strOut;
-
-        }
-        catch (HDF5Exception ex) {
-            String msg = "Error reading String dataset";
-            log.error(msg);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Read the entire dataset as an array of floats.
-     *
-     * @param datasetId
-     * @return
-     */
-    public float[] readAllFloats(int datasetId) {
-        try {
-            int size = (int) getDims(datasetId)[0];
-            float[] data = new float[size];
-            H5.H5Dread(datasetId, HDF5Constants.H5T_NATIVE_FLOAT, HDF5Constants.H5S_ALL,
-                    HDF5Constants.H5S_ALL, HDF5Constants.H5P_DEFAULT, data);
-            return data;
-        }
-        catch (HDF5Exception ex) {
-            String msg = "Error reading Float dataset";
-            log.error(msg);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Read the entire dataset as an array of ints.  The dsName is
-     * relative to the object identfed by groupId (file or group).
-     *
-     * @param datasetId
-     * @return
-     */
-    public int[] readAllInts(int datasetId) {
-        try {
-            int size = (int) getDims(datasetId)[0];
-            int[] data = new int[size];
-            H5.H5Dread(datasetId, HDF5Constants.H5T_NATIVE_INT, HDF5Constants.H5S_ALL,
-                    HDF5Constants.H5S_ALL, HDF5Constants.H5P_DEFAULT, data);
-            return data;
-        }
-        catch (HDF5Exception ex) {
-            String msg = "Error reading Int dataset";
-            log.error(msg);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Read a section of the dataset as an array of floats.
-     *
-     * @param datasetId
-     * @param fromIndex
-     * @param toIndex
-     * @return
-     */
-    public float[] readFloats(int datasetId, int fromIndex, int toIndex) {
-        try {
-
-            int dataspace = getDataspace(datasetId);
-
-            int nPts = toIndex - fromIndex + 1;
-            long[] offset = new long[1];
-            long[] count = new long[1];
-            offset[0] = fromIndex;
-            count[0] = nPts;
-            H5.H5Sselect_hyperslab(dataspace, HDF5Constants.H5S_SELECT_SET, offset, null, count,
-                    null);
-
-            // Step 2.   Create a memory dataspace
-            long[] memDims = new long[1];
-            memDims[0] = nPts;
-            int memspace = H5.H5Screate_simple(1, memDims, null);
-
-            // Step 3.  Create a "to" hyperslab
-            long[] memOffset = new long[1];
-            memOffset[0] = 0;
-            long[] memCount = new long[1];
-            memCount[0] = nPts;
-            H5.H5Sselect_hyperslab(memspace, HDF5Constants.H5S_SELECT_SET, memOffset, null,
-                    memCount, null);
-
-            // Step 4.  Read the data
-            float[] partialData = new float[nPts];
-            H5.H5Dread(datasetId, HDF5Constants.H5T_NATIVE_FLOAT, memspace, dataspace,
-                    HDF5Constants.H5P_DEFAULT, partialData);
-
-            return partialData;
-        }
-        catch (HDF5Exception ex) {
-            String msg = "Error reading floats from " + fromIndex + " to " + toIndex;
-            log.error(msg);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Read a section of the dataset as an array of floats.
-     *
-     * @param datasetId
-     * @param fromIndex
-     * @param toIndex
-     * @return
-     */
-    public double[] readDoubles(int datasetId, int fromIndex, int toIndex) {
-        try {
-
-            int dataspace = getDataspace(datasetId);
-
-            int nPts = toIndex - fromIndex + 1;
-            long[] offset = new long[1];
-            long[] count = new long[1];
-            offset[0] = fromIndex;
-            count[0] = nPts;
-            H5.H5Sselect_hyperslab(dataspace, HDF5Constants.H5S_SELECT_SET, offset, null, count,
-                    null);
-
-            // Step 2.   Create a memory dataspace
-            long[] memDims = new long[1];
-            memDims[0] = nPts;
-            int memspace = H5.H5Screate_simple(1, memDims, null);
-
-            // Step 3.  Create a "to" hyperslab
-            long[] memOffset = new long[1];
-            memOffset[0] = 0;
-            long[] memCount = new long[1];
-            memCount[0] = nPts;
-            H5.H5Sselect_hyperslab(memspace, HDF5Constants.H5S_SELECT_SET, memOffset, null,
-                    memCount, null);
-
-            // double  Step 4.  Read the data
-            double[] partialData = new double[nPts];
-            H5.H5Dread(datasetId, HDF5Constants.H5T_NATIVE_DOUBLE, memspace, dataspace,
-                    HDF5Constants.H5P_DEFAULT, partialData);
-
-            return partialData;
-        }
-        catch (HDF5Exception ex) {
-            String msg = "Error reading double from " + fromIndex + " to " + toIndex;
-            log.error(msg);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Method description
-     *
-     * @param datasetId
-     * @param fromIndex
-     * @param toIndex
-     * @return
-     */
-    public long[] readLongs(int datasetId, int fromIndex, int toIndex) {
-        try {
-
-            int dataspace = getDataspace(datasetId);
-
-            int nPts = toIndex - fromIndex + 1;
-            long[] offset = new long[1];
-            long[] count = new long[1];
-            offset[0] = fromIndex;
-            count[0] = nPts;
-            H5.H5Sselect_hyperslab(dataspace, HDF5Constants.H5S_SELECT_SET, offset, null, count,
-                    null);
-
-            // Step 2.   Create a memory dataspace
-            long[] memDims = new long[1];
-            memDims[0] = nPts;
-            int memspace = H5.H5Screate_simple(1, memDims, null);
-
-            // Step 3.  Create a "to" hyperslab
-            long[] memOffset = new long[1];
-            memOffset[0] = 0;
-            long[] memCount = new long[1];
-            memCount[0] = nPts;
-            H5.H5Sselect_hyperslab(memspace, HDF5Constants.H5S_SELECT_SET, memOffset, null,
-                    memCount, null);
-
-            // Step 4.  Read the data
-            long[] partialData = new long[nPts];
-            H5.H5Dread(datasetId, HDF5Constants.H5T_NATIVE_LLONG, memspace, dataspace,
-                    HDF5Constants.H5P_DEFAULT, partialData);
-
-            return partialData;
-        }
-        catch (HDF5Exception ex) {
-            String msg = "Error reading longs from " + fromIndex + " to " + toIndex;
-            log.error(msg);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Method description
-     *
-     * @param dsName
-     * @param fromIndex
-     * @param toIndex
-     * @return
-     */
-    public int[] readInts(String dsName, int fromIndex, int toIndex) {
-
-        int datasetId = openDataset(dsName);
-        int[] ints = readInts(datasetId, fromIndex, toIndex);
-        closeDataset(datasetId);
-        return ints;
-
-
-    }
-
-    /**
-     * Method description
-     *
-     * @param datasetId
-     * @param fromIndex
-     * @param toIndex
-     * @return
-     */
-    public int[] readInts(int datasetId, int fromIndex, int toIndex) {
-        try {
-
-            int dataspace = getDataspace(datasetId);
-
-            int nPts = toIndex - fromIndex + 1;
-            long[] offset = new long[1];
-            long[] count = new long[1];
-            offset[0] = fromIndex;
-            count[0] = nPts;
-            H5.H5Sselect_hyperslab(dataspace, HDF5Constants.H5S_SELECT_SET, offset, null, count,
-                    null);
-
-            // Step 2.   Create a memory dataspace
-            long[] memDims = new long[1];
-            memDims[0] = nPts;
-            int memspace = H5.H5Screate_simple(1, memDims, null);
-
-            // Step 3.  Create a "to" hyperslab
-            long[] memOffset = new long[1];
-            memOffset[0] = 0;
-            long[] memCount = new long[1];
-            memCount[0] = nPts;
-            H5.H5Sselect_hyperslab(memspace, HDF5Constants.H5S_SELECT_SET, memOffset, null,
-                    memCount, null);
-
-            // Step 4.  Read the data
-            int[] partialData = new int[nPts];
-            H5.H5Dread(datasetId, HDF5Constants.H5T_NATIVE_INT, memspace, dataspace,
-                    HDF5Constants.H5P_DEFAULT, partialData);
-
-            return partialData;
-        }
-        catch (HDF5Exception ex) {
-            String msg = "Error reading ints from " + fromIndex + " to " + toIndex;
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-
-    @Override
-    protected void finalize() throws Throwable {
-        super.finalize();
-        closeFile();
-    }
-}
diff --git a/src/org/broad/igv/h5/HDF5LocalWriter.java b/src/org/broad/igv/h5/HDF5LocalWriter.java
deleted file mode 100644
index 5bea0ef..0000000
--- a/src/org/broad/igv/h5/HDF5LocalWriter.java
+++ /dev/null
@@ -1,529 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * IGVTestReadH5Lib.java
- *
- * Tests reading an IGV H5 file using the low level C wrapper library.
- * This file maps java functions as directly as possible to the underlying
- * C functions.  It is much faster than the object API.
- */
-package org.broad.igv.h5;
-
-import ncsa.hdf.hdf5lib.H5;
-import ncsa.hdf.hdf5lib.HDF5Constants;
-import ncsa.hdf.hdf5lib.exceptions.HDF5Exception;
-import ncsa.hdf.hdf5lib.exceptions.HDF5LibraryException;
-import ncsa.hdf.hdf5lib.exceptions.HDF5SymbolTableException;
-import org.apache.log4j.Logger;
-
-import java.io.FileNotFoundException;
-import java.lang.reflect.Array;
-
-/**
- * @author jrobinso
- */
-public class HDF5LocalWriter implements HDFWriter {
-
-    private static Logger log = Logger.getLogger(HDF5LocalWriter.class);
-
-    public static int RDWR = ncsa.hdf.hdf5lib.HDF5Constants.H5F_ACC_RDWR;
-
-    public int createFile(String name) {
-        try {
-            return H5.H5Fcreate(name, HDF5Constants.H5F_ACC_TRUNC, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
-        } catch (HDF5LibraryException ex) {
-            String msg = "Error creating HDF5 file";
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-    }
-
-    /**
-     * Open an hdf5 file
-     */
-    public int openFile(String path) throws FileNotFoundException {
-        return openFile(path, RDWR);
-    }
-
-    /**
-     * Open an hdf5 file
-     */
-    public int openFile(String path, int mode) throws FileNotFoundException {
-        try {
-            return ncsa.hdf.hdf5lib.H5.H5Fopen(path, mode, ncsa.hdf.hdf5lib.HDF5Constants.H5P_DEFAULT);
-        } catch (HDF5LibraryException ex) {
-            String msg = "Error opening HDF5 file: " + path;
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-    }
-
-    /**
-     * Close an hdf5 file
-     */
-    public void closeFile(int fileId) {
-        try {
-            H5.H5Fclose(fileId);
-        } catch (HDF5LibraryException ex) {
-            String msg = "Error closing HDF5 file";
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-    }
-
-    public int openDataset(int fileId, String dsName) {
-
-        try {
-            return H5.H5Dopen(fileId, dsName);
-        } catch (HDF5SymbolTableException ex) {
-            throw new ObjectNotFoundException("Error opening dataset: " + dsName);
-        } catch (HDF5LibraryException ex) {
-            log.error("Error opening dataset", ex);
-            throw new RuntimeException(ex);
-        }
-
-    }
-
-    /**
-     * Close an hdf5 dataset
-     */
-    public void closeDataset(int datasetId) {
-
-        try {
-            H5.H5Dclose(datasetId);
-        } catch (HDF5LibraryException ex) {
-            log.error("Error closing dataset", ex);
-            throw new RuntimeException("Error closing dataset");
-        }
-
-    }
-
-    /**
-     * Create a group with the given name at the given location.
-     * nodeId - the handle to a file or another group.  If nodeId refers to a file
-     * name should be an absolute path.  If nodeId referst to a group
-     * name should be relative to that group.
-     * name - the name of the new group
-     *
-     * @returns the identifier for the newly created group.
-     */
-    public int createGroup(int locId, String name) {
-        try {
-            return H5.H5Gcreate(locId, name, 0);
-        } catch (HDF5LibraryException ex) {
-            String msg = "Error creating group: " + name;
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    public int openGroup(int locId, String name) {
-        try {
-            return H5.H5Gopen(locId, name);
-        } catch (ncsa.hdf.hdf5lib.exceptions.HDF5SymbolTableException ex) {
-            String msg = "Error opening group: " + name;
-            if (log.isDebugEnabled()) {
-                log.debug(msg, ex);
-            }
-            throw new ObjectNotFoundException(msg);
-        } catch (HDF5LibraryException ex) {
-            String msg = "Error opening group: " + name;
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Close an hdf5 group
-     */
-    public void closeGroup(int groupId) {
-        try {
-            H5.H5Gclose(groupId);
-        } catch (HDF5LibraryException ex) {
-            String msg = "Error closing HDF5 group";
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Create a dataset in the group identifed by nodeId
-     * dims = new long[]{nRows, nCols});
-     */
-    public int createDataset(int locId, String name, int typeId, long[] dims) {
-        try {
-            int dataspace = H5.H5Screate_simple(dims.length, dims, null);
-            return H5.H5Dcreate(locId, name, typeId, dataspace, HDF5Constants.H5P_DEFAULT);
-        } catch (HDF5Exception ex) {
-            String msg = "Error creating dataset: " + name;
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-    }
-
-    public void createAndWriteDataset(int nodeId, String dsName, float[][] data) {
-
-        int typeInfo = HDF5Constants.H5T_NATIVE_FLOAT;
-        int nRows = Array.getLength(data);
-        if (nRows == 0) {
-            log.info("Attempt to create zero length dataset: " + dsName);
-        } else {
-            long nCols = data[0].length;
-            long[] dims = new long[]{nRows, nCols};
-            int dataset = -1;
-            try {
-                if (dsName.contains("raw")) {
-                    log.info("Creating dataset " + dsName + " dims = " +
-                            nRows + ", " + nCols);
-                }
-                dataset = createDataset(nodeId, dsName, typeInfo, dims);
-                writeAllData(dataset, typeInfo, data);
-            } finally {
-                if (dataset > 0) {
-                    closeDataset(dataset);
-                }
-            }
-
-        }
-    }
-
-    /**
-     * Create a dataset, write the supplied data, and close the dataset.
-     * <p/>
-     * nodeId - parent node of the dataset, usually a group
-     * dsName - name of the dataset
-     * data - the data, either int[], long[], float[], double[], or String[].
-     * <p/>
-     * int dataspace = H5.H5Screate_simple(dims.nRows, dims, null);
-     * return H5.H5Dcreate(locId, name, typeId, dataspace, HDF5Constants.H5P_DEFAULT);
-     */
-    public void createAndWriteVectorDataset(int nodeId, String dsName, Object data) {
-        if (data instanceof String[]) {
-            createAndWriteStringDataset(nodeId, dsName, (String[]) data);
-        }
-
-        int typeInfo = getTypeForArray(data);
-        int length = Array.getLength(data);
-        if (length == 0) {
-            log.info("Attempt to create zero length dataset: " + dsName);
-        } else {
-            long[] dims = new long[]{length};
-            int dataset = -1;
-            try {
-                dataset = createDataset(nodeId, dsName, typeInfo, dims);
-                writeAllData(dataset, typeInfo, data);
-            } finally {
-                if (dataset > 0) {
-                    closeDataset(dataset);
-                }
-            }
-
-        }
-    }
-
-    /**
-     * Returns the HDF typeId and array nRows for an array.  Info is returned as
-     * a 2 element int array.  Yes, a class could be used but seems excessive
-     * for this private method.
-     */
-    private int getTypeForArray(Object array) {
-
-        if (array instanceof Integer || array instanceof int[] || array instanceof int[][]) {
-            return HDF5Constants.H5T_NATIVE_INT;
-        }
-
-        if (array instanceof Long || array instanceof long[] || array instanceof long[][]) {
-            return HDF5Constants.H5T_NATIVE_LLONG;
-        }
-
-        if (array instanceof Float || array instanceof float[] || array instanceof float[][]) {
-            return HDF5Constants.H5T_NATIVE_FLOAT;
-        }
-
-        if (array instanceof Number || array instanceof double[] || array instanceof double[][]) {
-            return HDF5Constants.H5T_NATIVE_DOUBLE;
-        }
-
-        if (array instanceof char[]) {
-            return createStringDatatype(1);
-        }
-
-        String msg = "Error: No HDF Type for: " + array.getClass().getName();
-        log.error(msg);
-        throw new DataAccessException("No HDF Type for: " + array.getClass().getName());
-
-    }
-
-    /**
-     * Create a dataset of  strings.
-     * // TODO -- look at H5DwriteString
-     */
-    public void createAndWriteStringDataset(int locId, String dsName, String[] stringArray) {
-        int maxSize = 0;
-        for (String string : stringArray) {
-            maxSize = Math.max(maxSize, string.length());
-        }
-
-        createAndWriteStringDataset(locId, dsName, stringArray, maxSize);
-
-    }
-
-    /**
-     * Create a dataset of fixed width strings
-     * //TODO look at
-     */
-    public void createAndWriteStringDataset(int locId, String dsName, String[] stringArray, int stringSize) {
-        StringBuffer sb = new StringBuffer(stringArray.length * stringSize);
-        for (int i = 0; i <
-                stringArray.length; i++) {
-            char[] chars = new char[stringSize];
-
-            char[] tmp = stringArray[i].toCharArray();
-            System.arraycopy(tmp, 0, chars, 0, tmp.length);
-            sb.append(new String(chars));
-        }
-
-        byte[] buf = sb.toString().getBytes();
-
-        // Reading string datatypes is not supported in the java api
-        int dataType = createStringDatatype(stringSize);
-        long[] dims = new long[]{stringArray.length};
-
-        //int dataType = HDF5Constants.H5T_NATIVE_CHAR;
-        //long[] dims = new long[]{stringArray.nRows, stringSize};
-
-        int nameDS = -1;
-        try {
-            nameDS = createDataset(locId, dsName, dataType, dims);
-            writeAllData(nameDS, dataType, buf);
-        } finally {
-            if (nameDS > 0) {
-                closeDataset(nameDS);
-            }
-        }
-
-    }
-
-    public int createStringDatatype(int strLength) {
-        try {
-            int tid = H5.H5Tcopy(HDF5Constants.H5T_C_S1);
-            H5.H5Tset_size(tid, strLength);
-            return tid;
-        } catch (HDF5LibraryException ex) {
-            String msg = "Error creating string datatype. Length = " + strLength;
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Write to a dataset
-     * datasetId - dataset handle
-     * data -  an array of primitives matching the data typeId
-     * <p/>
-     * *
-     */
-    public void writeAllData(int datasetId, int type, Object data) {
-        try {
-            int status = H5.H5Dwrite(datasetId, type, HDF5Constants.H5S_ALL,
-                    HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, data);
-            if (status != 0) {
-                throw new DataAccessException("Error writing data: " + status);
-            }
-
-        } catch (HDF5Exception ex) {
-            String msg = "Error writing data";
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Convenience function for writing a row of float data to an assumed 2-d
-     * dataset.
-     * // TODO -- verification, check that dataset shape is 2-D, rowId and data.nRows are in bounds
-     *
-     * @param datasetId
-     * @param rowId
-     * @param data
-     */
-    public void writeDataRow(int datasetId, int rowId, float[] data, int length) {
-        try {
-
-            int dataspace = getDataspace(datasetId);
-
-            long[] offset = new long[2];
-            long[] count = new long[2];
-            offset[0] = rowId;
-            offset[1] = 0;
-            count[0] = 1;
-            count[1] = length;
-
-            int status1 = H5.H5Sselect_hyperslab(dataspace, HDF5Constants.H5S_SELECT_SET, offset, null, count, null);
-            if (status1 != 0) {
-                log.error("Error selecting hyberslab  Error code:" + status1);
-                throw new DataAccessException("Error selecting hyberslab.  Error code: " + status1);
-            }
-
-            int memspace = H5.H5Screate_simple(1, new long[]{length}, null); //HDF5Constants.H5S_ALL;
-
-            int type = getTypeForArray(data);
-
-            int status = H5.H5Dwrite(datasetId, type, memspace,
-                    dataspace, HDF5Constants.H5S_ALL, data);
-
-            H5.H5Sclose(dataspace);
-
-            if (status != 0) {
-                throw new DataAccessException("Error writing data: " + status);
-            }
-
-        } catch (HDF5Exception ex) {
-            String msg = "Error writing data row.  rowId= " + rowId + " length= " + length;
-            ex.printStackTrace();
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Convenience function for writing a single float value to an assumed 1-d
-     * dataset.
-     * // TODO -- verification, check that dataset shape is 1-D, cellId in bounds
-     *
-     * @param datasetId
-     * @param rowId
-     * @param data
-     */
-    public void writeDataValue(int datasetId, int cellId, float value) {
-        try {
-
-            int dataspace = getDataspace(datasetId);
-
-            long[] offset = new long[1];
-            long[] count = new long[1];
-            offset[0] = cellId;
-            count[0] = 1;
-            int status1 = H5.H5Sselect_hyperslab(dataspace, HDF5Constants.H5S_SELECT_SET, offset, null, count, null);
-
-            int memspace = H5.H5Screate_simple(1, new long[]{1}, null); //HDF5Constants.H5S_ALL;
-
-            int type = HDF5Constants.H5T_NATIVE_FLOAT;
-
-            int status = H5.H5Dwrite(datasetId, type, memspace, dataspace,
-                    HDF5Constants.H5S_ALL, new float[]{value});
-
-            H5.H5Sclose(dataspace);
-
-            if (status != 0) {
-                throw new DataAccessException("Error writing data: " + status);
-            }
-
-        } catch (HDF5Exception ex) {
-            String msg = "Error writing data value.  cellId= " + cellId + " value= " + value;
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    public int deleteAttribute(int locId, String name) {
-        try {
-            int herr = H5.H5Adelete(locId, name);
-            return herr;
-        } catch (HDF5LibraryException ex) {
-            String msg = "Error deleting attribute.  locId= " + locId + " name= " + name;
-            //log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-    }
-
-    /**
-     * Write the attribute (string/value pair).  The method attempts to assign
-     * the correct HDF5 typeId based on the runtime class of the value.  The rules
-     * for typeId assignement are
-     * Integer (int)     =>  H5T_NATIVE_INT
-     * Long (long)       =>  H5T_NATIVE_LLONG
-     * Float (float)     =>  H5T_NATIVE_FLOAT
-     * All other numeric =>  H5T_NATIVE_DOUBLE
-     * String            =>  H5T_C_S1
-     */
-    public int writeAttribute(int locId, String name, Object value) {
-        try {
-            int dataType = 0;
-            Object buf = null;
-            if (value instanceof Integer) {
-                dataType = HDF5Constants.H5T_NATIVE_INT;
-                buf =
-                        new int[]{((Number) value).intValue()};
-            } else if (value instanceof Long) {
-                dataType = HDF5Constants.H5T_NATIVE_LLONG;
-                buf =
-                        new long[]{((Number) value).longValue()};
-            } else if (value instanceof Float) {
-                dataType = HDF5Constants.H5T_NATIVE_FLOAT;
-                buf =
-                        new float[]{((Number) value).floatValue()};
-            } else if (value instanceof Number) {
-                dataType = HDF5Constants.H5T_NATIVE_DOUBLE;
-                buf =
-                        new double[]{((Number) value).doubleValue()};
-            } else {
-                dataType = H5.H5Tcopy(H5.J2C(HDF5Constants.H5T_C_S1));
-                buf =
-                        value.toString().getBytes();
-                H5.H5Tset_size(dataType, ((byte[]) buf).length);
-            }
-
-            int spaceId = H5.H5Screate(HDF5Constants.H5S_SCALAR);
-            int attrId = H5.H5Acreate(locId, name, dataType, spaceId, HDF5Constants.H5P_DEFAULT);
-            int status = H5.H5Awrite(attrId, dataType, buf);
-            status =
-                    H5.H5Sclose(spaceId);  // TODO check status
-            status =
-                    H5.H5Aclose(attrId);  // TODO check status
-            return status;
-        } catch (HDF5Exception ex) {
-            String msg = "Error writing to dataset ";
-            log.error(msg, ex);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-
-    /**
-     * Return the dataspace for the dataset
-     */
-    private int getDataspace(int datasetId) {
-        try {
-            return H5.H5Dget_space(datasetId);
-        } catch (HDF5LibraryException ex) {
-            String msg = "Error getting dataspace";
-            log.error(msg);
-            throw new DataAccessException(msg, ex);
-        }
-
-    }
-}
diff --git a/src/org/broad/igv/h5/HDF5Reader.java b/src/org/broad/igv/h5/HDF5Reader.java
index 3c43f27..f0b1b4d 100644
--- a/src/org/broad/igv/h5/HDF5Reader.java
+++ b/src/org/broad/igv/h5/HDF5Reader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -18,6 +18,8 @@
 
 package org.broad.igv.h5;
 
+import java.util.List;
+
 /**
  * An HDF55 reader handles read access to an HDF5 file,  either local or
  * remote.
@@ -31,7 +33,6 @@ public interface HDF5Reader {
     /**
      * Open an HDF5 dataset in the given file.
      *
-     * @param fileId      - the HDF5 handle to the file
      * @param datasetName
      * @return a handle to the dataset
      */
@@ -45,7 +46,6 @@ public interface HDF5Reader {
     /**
      * Open an HDF5 data group.
      *
-     * @param parentNodeId - HDF5 handle to a parent object.  The group path is relative to this node
      * @param groupPath
      * @return
      */
@@ -64,7 +64,6 @@ public interface HDF5Reader {
      * @param groupId
      * @param attrName
      * @return
-     * @throws org.broad.igv.exception.ObjectNotFoundException
      *
      */
     public int readIntegerAttribute(int groupId, String attrName) throws ObjectNotFoundException;
@@ -75,7 +74,6 @@ public interface HDF5Reader {
      * @param groupId
      * @param attrName
      * @return
-     * @throws org.broad.igv.exception.ObjectNotFoundException
      *
      */
     public String readStringAttribute(int groupId, String attrName);
@@ -111,7 +109,7 @@ public interface HDF5Reader {
      * @param datasetId
      * @return
      */
-    String[] readAllStrings(int datasetId);
+    List<String> readAllStrings(int datasetId);
 
     /**
      * Read a section of the dataset as an array of floats.
diff --git a/src/org/broad/igv/h5/HDF5ReaderFactory.java b/src/org/broad/igv/h5/HDF5ReaderFactory.java
index fb1b0d0..f9f1854 100644
--- a/src/org/broad/igv/h5/HDF5ReaderFactory.java
+++ b/src/org/broad/igv/h5/HDF5ReaderFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,6 +22,7 @@
  */
 package org.broad.igv.h5;
 
+import org.broad.igv.exceptions.DataLoadException;
 import org.broad.igv.util.ResourceLocator;
 
 import java.io.File;
@@ -39,11 +40,8 @@ public class HDF5ReaderFactory {
      */
     public static HDF5Reader getReader(ResourceLocator locator) {
         if (locator.isLocal()) {
-            if (locator.getPath().endsWith(".h5")) {
-                return new HDF5LocalReader(locator.getPath());
-            } else {
-                return new H5BinReader(new File(locator.getPath()));
-            }
+            throw new DataLoadException("HDF5 files are no longer supported.  " +
+                    "Contact igv-help at broadinstitute.org for more information.", locator.getPath());
         } else {
             return new HDF5RemoteReader(locator);
         }
diff --git a/src/org/broad/igv/h5/HDF5RemoteReader.java b/src/org/broad/igv/h5/HDF5RemoteReader.java
index 135ef9e..2ca39f9 100644
--- a/src/org/broad/igv/h5/HDF5RemoteReader.java
+++ b/src/org/broad/igv/h5/HDF5RemoteReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -25,16 +25,13 @@ package org.broad.igv.h5;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
-import org.broad.igv.data.StringArrayList;
+import org.broad.igv.util.IGVHttpUtils;
 import org.broad.igv.util.ResourceLocator;
 
 import java.io.*;
 import java.net.URL;
 import java.net.URLConnection;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * @author jrobinso
@@ -73,7 +70,7 @@ public class HDF5RemoteReader implements HDF5Reader {
 
             try {
                 URL url = new URL(server + "?method=closeFiles&fileId=" + fileIdString);
-                URLConnection connection = url.openConnection();
+                URLConnection connection = IGVHttpUtils.openConnection(url);
                 connection.getInputStream().read();
 
             }
@@ -111,8 +108,7 @@ public class HDF5RemoteReader implements HDF5Reader {
             this.locator = locator;
             URL url = new URL(locator.getServerURL() + "?method=openFile&file="
                     + locator.getPath());
-            URLConnection connection = url.openConnection();
-            urlStream = connection.getInputStream();
+            urlStream = IGVHttpUtils.openConnectionStream(url);
             DataInputStream is = new DataInputStream(new BufferedInputStream(urlStream));
             fileId = is.readInt();
             recordFile(locator.getServerURL(), fileId);
@@ -210,7 +206,7 @@ public class HDF5RemoteReader implements HDF5Reader {
         try {
             URL url = new URL(locator.getServerURL() + "?method=readIntegerAttribute&id=" + groupId
                     + "&name=" + attrName);
-            URLConnection connection = url.openConnection();
+            URLConnection connection = IGVHttpUtils.openConnection(url);
             urlStream = connection.getInputStream();
             DataInputStream is = new DataInputStream(new BufferedInputStream(urlStream));
             int value = is.readInt();
@@ -251,7 +247,7 @@ public class HDF5RemoteReader implements HDF5Reader {
         InputStream urlStream = null;
         try {
             URL url = new URL(locator.getServerURL() + "?method=readStringAttribute&id=" + groupId + "&name=" + attrName);
-            URLConnection connection = url.openConnection();
+            URLConnection connection = IGVHttpUtils.openConnection(url);
             urlStream = connection.getInputStream();
 
             BufferedReader reader = new BufferedReader(new InputStreamReader(urlStream));
@@ -288,7 +284,7 @@ public class HDF5RemoteReader implements HDF5Reader {
         try {
             URL url = new URL(locator.getServerURL() + "?method=readDoubleAttribute&id=" + groupId
                     + "&name=" + attrName);
-            URLConnection connection = url.openConnection();
+            URLConnection connection = IGVHttpUtils.openConnection(url);
             urlStream = connection.getInputStream();
 
             DataInputStream is = new DataInputStream(new BufferedInputStream(urlStream));
@@ -321,7 +317,7 @@ public class HDF5RemoteReader implements HDF5Reader {
         InputStream urlStream = null;
         try {
             URL url = new URL(locator.getServerURL() + "?method=readAllFloats&id=" + datasetId);
-            URLConnection connection = url.openConnection();
+            URLConnection connection = IGVHttpUtils.openConnection(url);
 
             int byteLength = connection.getContentLength();
 
@@ -365,7 +361,7 @@ public class HDF5RemoteReader implements HDF5Reader {
         InputStream urlStream = null;
         try {
             URL url = new URL(locator.getServerURL() + "?method=readAllInts&id=" + datasetId);
-            URLConnection connection = url.openConnection();
+            URLConnection connection = IGVHttpUtils.openConnection(url);
 
             // Compute the # of ints in the stream.
             int byteLength = connection.getContentLength();
@@ -403,13 +399,13 @@ public class HDF5RemoteReader implements HDF5Reader {
      * @param datasetId
      * @return
      */
-    public String[] readAllStrings(int datasetId) {
+    public List<String> readAllStrings(int datasetId) {
         InputStream urlStream = null;
         try {
             URL url = new URL(locator.getServerURL() + "?method=readAllStrings&id=" + datasetId);
-            URLConnection connection = url.openConnection();
+            URLConnection connection = IGVHttpUtils.openConnection(url);
 
-            StringArrayList strings = new StringArrayList(100);
+            ArrayList<String> strings = new ArrayList(100);
 
             urlStream = connection.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(urlStream));
@@ -423,7 +419,7 @@ public class HDF5RemoteReader implements HDF5Reader {
             }
             reader.close();
 
-            return strings.toArray();
+            return  strings;
 
         }
         catch (IOException ex) {
@@ -455,7 +451,7 @@ public class HDF5RemoteReader implements HDF5Reader {
         try {
             URL url = new URL(locator.getServerURL() + "?method=readFloats&id=" + datasetId
                     + "&from=" + fromIndex + "&to=" + toIndex);
-            URLConnection connection = url.openConnection();
+            URLConnection connection = IGVHttpUtils.openConnection(url);
 
             int byteLength = connection.getContentLength();
             int nFloats = byteLength / 4;
@@ -499,7 +495,7 @@ public class HDF5RemoteReader implements HDF5Reader {
         try {
             URL url = new URL(locator.getServerURL() + "?method=readInts&id=" + datasetId
                     + "&from=" + fromIndex + "&to=" + toIndex);
-            URLConnection connection = url.openConnection();
+            URLConnection connection = IGVHttpUtils.openConnection(url);
 
             int byteLength = connection.getContentLength();
             int nInts = byteLength / 4;
@@ -547,7 +543,7 @@ public class HDF5RemoteReader implements HDF5Reader {
         try {
             URL url = new URL(locator.getServerURL() + "?method=readDataSlice&id=" + datasetId
                     + "&from=" + fromIndex + "&to=" + toIndex);
-            URLConnection connection = url.openConnection();
+            URLConnection connection = IGVHttpUtils.openConnection(url);
 
             urlStream = connection.getInputStream();
             DataInputStream is = new DataInputStream(new BufferedInputStream(urlStream));
@@ -591,8 +587,7 @@ public class HDF5RemoteReader implements HDF5Reader {
         InputStream urlStream = null;
         try {
             URL url = new URL(locator.getServerURL() + "?method=" + method + "&id=" + datasetId);
-            URLConnection connection = url.openConnection();
-            urlStream = connection.getInputStream();
+            urlStream = IGVHttpUtils.openConnectionStream(url);
 
             // TODO -- read result;
         }
@@ -638,7 +633,7 @@ public class HDF5RemoteReader implements HDF5Reader {
 
             URL url = new URL(locator.getServerURL() + "?method=" + method + "&id=" + parentNodeId
                     + "&name=" + name);
-            URLConnection connection = url.openConnection();
+            URLConnection connection = IGVHttpUtils.openConnection(url);
             connection.setDoOutput(true);
 
             urlStream = connection.getInputStream();
diff --git a/src/org/broad/igv/h5/HDFUtilsExamples.java b/src/org/broad/igv/h5/HDFUtilsExamples.java
deleted file mode 100644
index 99bc115..0000000
--- a/src/org/broad/igv/h5/HDFUtilsExamples.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and openFile the template in the editor.
- */
-package org.broad.igv.h5;
-
-import ncsa.hdf.hdf5lib.exceptions.HDF5LibraryException;
-import org.apache.log4j.Logger;
-
-import java.io.FileNotFoundException;
-
-/**
- * Class of examples to illustrate use of the HDF5LocalReader.  Also used for timing
- * tests.
- */
-public class HDFUtilsExamples {
-
-    private static Logger log = Logger.getLogger(HDFUtilsExamples.class);
-
-    private static HDF5LocalWriter writer = new HDF5LocalWriter();
-
-    public static void main(String[] args) throws FileNotFoundException, HDF5LibraryException {
-        //getDataHeaders();
-        //testMatrix();
-        //writeRows();
-        //readRow();
-        //writeStringDataset();
-        //readStringDataset();
-    }
-
-
-    public static void writeStringDataset() {
-
-        String[] strings = {"a", "variable", "length", "string", "array"};
-        int fileId = writer.createFile("testStrings.h5");
-        int groupId = writer.createGroup(fileId, "data");
-        writer.writeAttribute(groupId, "attr", "value");
-        writer.createAndWriteStringDataset(groupId, "testStrings", strings);
-        writer.closeGroup(groupId);
-        writer.closeFile(fileId);
-
-    }
-
-    public static void readStringDataset() {
-
-        HDF5LocalReader reader = new HDF5LocalReader("testStrings.h5");
-        int datasetId = reader.openDataset("/data/testStrings");
-        int groupId = reader.openGroup("data");
-        System.out.println(reader.readStringAttribute(groupId, "attr"));
-        String[] strings = reader.readAllStrings(datasetId);
-        for (int i = 0; i < strings.length; i++) {
-            System.out.println(strings[i]);
-        }
-
-        reader.closeDataset(datasetId);
-        reader.closeGroup(groupId);
-        reader.closeFile();
-
-    }
-
-    public static void readRow() {
-
-
-        int row = 5;
-        int startCol = 10;
-        int endCol = 710;
-
-        HDF5LocalReader reader = new HDF5LocalReader("test.h5");
-        String dsName = "/data/data_chr6_z5";
-
-        long t0 = System.currentTimeMillis();
-        int datasetId = reader.openDataset(dsName);
-        long dt = System.currentTimeMillis() - t0;
-        System.out.println("DS open time = " + dt);
-
-        long t1 = System.currentTimeMillis();
-        float[] floats = reader.readDataRowSlice(datasetId, row, startCol, endCol);
-        dt = System.currentTimeMillis() - t1;
-        System.out.println("dt = " + dt);
-
-        reader.closeDataset(datasetId);
-        reader.closeFile();
-
-
-    }
-
-    public static void readRow2() {
-
-        int startCol = 10;
-        int endCol = 710;
-
-        //int fileId = reader.openFile("/Volumes/xchip_tcga/gbm/visualization/20071126_Demo_StagingArea/cn2.h5", reader.RDONLY);
-        HDF5LocalReader reader = new HDF5LocalReader("cn2.h5");
-        String dsName = "/data/chr1/z4/median";
-
-        long t0 = System.currentTimeMillis();
-        int datasetId = reader.openDataset(dsName);
-        long dt = System.currentTimeMillis() - t0;
-        System.out.println("DS open time = " + dt);
-
-        long t1 = System.currentTimeMillis();
-        float[][] floats = reader.readDataSlice(datasetId, startCol, endCol);
-        dt = System.currentTimeMillis() - t1;
-        System.out.println("dt = " + dt + "   # floats = " + (floats.length * floats[0].length));
-
-        long t2 = System.currentTimeMillis();
-        int dataSize = 0;
-        for (int row = 0; row < 284; row++) {
-            float[] floats2 = reader.readDataRowSlice(datasetId, row, startCol, endCol);
-            dataSize += floats2.length;
-        }
-        dt = System.currentTimeMillis() - t2;
-        System.out.println("dt = " + dt + "   # floats = " + dataSize);
-
-        reader.closeDataset(datasetId);
-        reader.closeFile();
-
-
-    }
-}
-  
-    
-
diff --git a/src/org/broad/igv/h5/HDFWriter.java b/src/org/broad/igv/h5/HDFWriter.java
deleted file mode 100644
index 096581f..0000000
--- a/src/org/broad/igv/h5/HDFWriter.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-package org.broad.igv.h5;
-
-/**
- * @author jrobinso
- */
-public interface HDFWriter {
-
-    /**
-     * Close an hdf5 dataset
-     */
-    void closeDataset(int datasetId);
-
-    /**
-     * Close an hdf5 file
-     */
-    void closeFile(int fileId);
-
-    /**
-     * Close an hdf5 group
-     */
-    void closeGroup(int groupId);
-
-    /**
-     * Create a dataset of  strings.
-     * // TODO -- look at H5DwriteString
-     */
-    void createAndWriteStringDataset(int locId, String dsName, String[] stringArray);
-
-    /**
-     * Create a dataset of fixed width strings
-     * //TODO look at
-     */
-    void createAndWriteStringDataset(int locId, String dsName, String[] stringArray, int stringSize);
-
-    public void createAndWriteDataset(int nodeId, String dsName, float[][] data);
-
-
-    /**
-     * Create a dataset, write the supplied data, and closeFile the dataset.
-     * locId - parent object of the dataset, usually a group
-     * dsName - name of the dataset
-     * data - the data, either int[], long[], float[], double[], or String[].
-     */
-    void createAndWriteVectorDataset(int locId, String dsName, Object data);
-
-    /**
-     * Create a dataset in the group identifed by locId
-     */
-    int createDataset(int locId, String name, int typeId, long[] dims);
-
-    int createFile(String name);
-
-    /**
-     * Create a group with the given name at the given location.
-     * locId - the handle to a file or another group.  If locId refers to a file
-     * name should be an absolute path.  If locId referst to a group
-     * name should be relative to that group.
-     * name - the name of the new group
-     *
-     * @returns the identifier for the newly created group.
-     */
-    int createGroup(int locId, String name);
-
-    int openDataset(int fileId, String dsName);
-
-    int openGroup(int locId, String name);
-
-    /**
-     * Convenience function for writing a row of float data to an assumed 2-d
-     * dataset.
-     * // TODO -- verification, check that dataset shape is 2-D, rowId and data.length are in bounds
-     *
-     * @param datasetId
-     * @param rowId
-     * @param data
-     */
-    public void writeDataRow(int datasetId, int rowId, float[] data, int length);
-
-    /**
-     * Convenience function for writing a single float value to an assumed 1-d
-     * dataset.
-     * // TODO -- verification, check that dataset shape is 1-D, cellId in bounds
-     *
-     * @param datasetId
-     * @param rowId
-     * @param data
-     */
-    public void writeDataValue(int datasetId, int cellId, float value);
-
-
-    /**
-     * Write the attribute (string/value pair).  The method attempts to assign
-     * the correct HDF5 typeId based on the runtime class of the value.  The rules
-     * for typeId assignement are
-     * Integer (int)     =>  H5T_NATIVE_INT
-     * Long (long)       =>  H5T_NATIVE_LLONG
-     * Float (float)     =>  H5T_NATIVE_FLOAT
-     * All other numeric =>  H5T_NATIVE_DOUBLE
-     * String            =>  H5T_C_S1
-     */
-    int writeAttribute(int locId, String name, Object value);
-
-}
diff --git a/src/org/broad/igv/h5/ObjectNotFoundException.java b/src/org/broad/igv/h5/ObjectNotFoundException.java
index 9df45b5..f1d3cf2 100644
--- a/src/org/broad/igv/h5/ObjectNotFoundException.java
+++ b/src/org/broad/igv/h5/ObjectNotFoundException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/h5/ZipBase.java b/src/org/broad/igv/h5/ZipBase.java
deleted file mode 100644
index aa0c080..0000000
--- a/src/org/broad/igv/h5/ZipBase.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.h5;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * @author jrobinso
- */
-public class ZipBase {
-
-    private Map<String, Integer> entityIdMap = new HashMap();
-
-    private Map<Integer, String> idEntityMap = new HashMap();
-
-    protected String getFullName(int locId, String name) {
-
-        // Names that start with / are absolute already
-        if (name.startsWith("/")) {
-            return name;
-        }
-
-        String entityName = idEntityMap.get(locId);
-        if (!entityName.endsWith("/")) {
-            entityName = entityName + "/";
-        }
-        String fullName = entityName + name;
-        return fullName;
-    }
-
-    protected void addEntity(Integer id, String name) {
-        idEntityMap.put(id, name);
-        entityIdMap.put(name, id);
-
-    }
-
-    protected int getEntityId(String name) {
-        return entityIdMap.get(name);
-    }
-
-    protected String getEntityName(int id) {
-        return idEntityMap.get(id);
-    }
-
-    protected void removeEntity(int id) {
-        String name = idEntityMap.get(id);
-        idEntityMap.remove(id);
-        entityIdMap.remove(name);
-    }
-}
diff --git a/src/org/broad/igv/h5/ZipReader.java b/src/org/broad/igv/h5/ZipReader.java
deleted file mode 100644
index ad0ea45..0000000
--- a/src/org/broad/igv/h5/ZipReader.java
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.h5;
-
-import java.io.*;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-//import org.apache.tools.zip.ZipEntry;
-//import org.apache.tools.zip.ZipFile;
-
-/**
- * @author jrobinso
- */
-public class ZipReader extends ZipBase implements HDF5Reader {
-
-    int fileId;
-
-    ZipFile zipFile;
-
-    Map<Integer, ZipEntry> zipEntries = new HashMap();
-
-    Map<Integer, Map<String, String>> attributeMaps = new HashMap();
-
-    public ZipReader(String filename) {
-        openFile(filename);
-    }
-
-    Map<String, String> getAttributes() {
-        Map<String, String> attrs = attributeMaps.get(fileId);
-        if (attrs == null) {
-            attrs = readAttributes();
-            attributeMaps.put(fileId, attrs);
-        }
-        return attrs;
-    }
-
-    private Map<String, String> readAttributes() {
-        String entryName = "attributes";
-        ZipEntry entry = zipFile.getEntry(entryName);
-        List<String> lines = readAsStrings(zipFile, entry);
-
-        Map<String, String> map = new HashMap(lines.size());
-        for (String kv : lines) {
-            String[] tokens = kv.split("\t");
-            map.put(tokens[0], tokens[1]);
-        }
-        return map;
-
-    }
-
-    private int openFile(String path) {
-        try {
-            fileId = path.hashCode();
-            zipFile = new ZipFile(new File(path));
-            addEntity(fileId, "/");
-
-
-            return fileId;
-        } catch (IOException ex) {
-            ex.printStackTrace();
-            throw new RuntimeException(ex);
-        }
-    }
-
-    public void closeFile() {
-        try {
-            zipFile.close();
-            removeEntity(fileId);
-
-            //TODO -- need to remove all entities associated with this file
-        } catch (IOException ex) {
-            ex.printStackTrace();
-            throw new RuntimeException(ex);
-        }
-    }
-
-    public int openDataset(String datasetName) {
-
-        String entryName = datasetName;
-        ZipEntry entry = zipFile.getEntry(entryName);
-        int id = entry.hashCode();
-        addEntity(id, datasetName);
-        zipEntries.put(id, entry);
-        return id;
-    }
-
-    public void closeDataset(int datasetId) {
-        zipEntries.remove(datasetId);
-    }
-
-    public int openGroup(String groupPath) {
-        // Nothing to do here other than create the path name and cache it.
-        String fullName = getFullName(fileId, groupPath);
-        int id = fullName.hashCode();
-        addEntity(id, fullName);
-
-        return id;
-    }
-
-    public void closeGroup(int groupId) {
-        removeEntity(groupId);
-    }
-
-    public String[] getChildNames(String groupPath) {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    public int readIntegerAttribute(int groupId, String attrName) throws ObjectNotFoundException {
-        return Integer.parseInt(readStringAttribute(groupId, attrName));
-    }
-
-    public String readStringAttribute(int groupId, String attrName) {
-        String groupName = getEntityName(groupId);
-        String fullAttrName = groupName + "|" + attrName;
-        return this.getAttributes().get(fullAttrName);
-    }
-
-    public double readDoubleAttribute(int groupId, String attrName) {
-        return Double.parseDouble(readStringAttribute(groupId, attrName));
-    }
-
-    public float[] readAllFloats(int datasetId) {
-        String entryName = getEntityName(datasetId);
-        ZipEntry entry = zipFile.getEntry(entryName);
-        return readAsFloats(zipFile, entry);
-
-    }
-
-    public int[] readAllInts(int datasetId) {
-        String entryName = getEntityName(datasetId);
-        ZipEntry entry = zipFile.getEntry(entryName);
-        return readAsInts(zipFile, entry);
-    }
-
-    public String[] readAllStrings(int datasetId) {
-        String entryName = getEntityName(datasetId);
-        ZipEntry entry = zipFile.getEntry(entryName);
-        return readAsStrings(zipFile, entry).toArray(new String[]{});
-    }
-
-    public float[] readFloats(int datasetId, int fromIndex, int toIndex) {
-        String entryName = getEntityName(datasetId);
-        ZipEntry entry = zipFile.getEntry(entryName);
-        return readAsFloats(zipFile, entry, fromIndex, toIndex);
-    }
-
-    public int[] readInts(int datasetId, int fromIndex, int toIndex) {
-        String entryName = getEntityName(datasetId);
-        ZipEntry entry = zipFile.getEntry(entryName);
-        return readAsInts(zipFile, entry, fromIndex, toIndex);
-    }
-
-    //////////// TODO 
-    // This implementation is restricted to  1-d datasets.   
-    public float[][] readDataSlice(int datasetId, int fromIndex, int toIndex) {
-        String entryName = getEntityName(datasetId);
-        ZipEntry entry = zipFile.getEntry(entryName);
-        float[] data = readAsFloats(zipFile, entry, fromIndex, toIndex);
-
-        return new float[][]{data};
-
-    }
-
-    private List<String> readAsStrings(ZipFile file, ZipEntry entry) {
-        List<String> strings = new ArrayList();
-        try {
-            BufferedReader br = new BufferedReader(new InputStreamReader(file.getInputStream(entry)));
-            String nextLine = null;
-            while ((nextLine = br.readLine()) != null) {
-                strings.add(nextLine.trim());
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-        return strings;
-
-    }
-
-    /**
-     * Read the contents of a zip entry as an array of ints.
-     */
-    private int[] readAsInts(ZipFile file, ZipEntry entry) {
-        try {
-
-            DataInputStream is = new DataInputStream(
-                    new BufferedInputStream(file.getInputStream(entry)));
-
-            int nInts = (int) (entry.getSize() / 4);
-            int[] ints = new int[nInts];
-            for (int i = 0; i < nInts; i++) {
-                ints[i] = is.readInt();
-            }
-
-            return ints;
-        } catch (IOException e) {
-            e.printStackTrace();
-            return null;
-        }
-
-    }
-
-    /**
-     * Read the contents of a zip entry as an array of ints.
-     */
-    private int[] readAsInts(ZipFile file, ZipEntry entry, int startIndex, int endIndex) {
-        try {
-
-            DataInputStream is = new DataInputStream(
-                    new BufferedInputStream(file.getInputStream(entry)));
-
-            int nPts = endIndex - startIndex;
-            assert nPts <= (entry.getSize() / 4);
-
-            int startPosition = startIndex * 4;
-            int nBytes = nPts * 4;
-            byte[] bytes = this.readBytes(is, startPosition, nBytes);
-
-            return convertBytesToInts(bytes);
-
-        } catch (IOException e) {
-            e.printStackTrace();
-            return null;
-        }
-
-    }
-
-    /**
-     * Read the content of a zip entry as an array of floats.
-     */
-    private float[] readAsFloats(ZipFile file, ZipEntry entry) {
-        try {
-            DataInputStream is = new DataInputStream(
-                    new BufferedInputStream(file.getInputStream(entry)));
-
-            int nInts = (int) (entry.getSize() / 4);
-            float[] values = new float[nInts];
-            for (int i = 0; i < nInts; i++) {
-                values[i] = is.readFloat();
-            }
-
-            return values;
-        } catch (IOException e) {
-            e.printStackTrace();
-            return null;
-        }
-    }
-
-    /**
-     * Read the content of a zip entry as an array of floats.
-     */
-    private float[] readAsFloats(ZipFile file, ZipEntry entry, int startIndex, int endIndex) {
-        try {
-            DataInputStream is = new DataInputStream(
-                    new BufferedInputStream(file.getInputStream(entry)));
-
-            int nPts = endIndex - startIndex;
-            assert nPts <= (entry.getSize() / 4);
-            float[] values = new float[nPts];
-
-            int startByte = startIndex * 4;
-            is.skipBytes(startByte);
-            for (int i = 0; i < nPts; i++) {
-                values[i] = is.readFloat();
-            }
-
-            return values;
-        } catch (IOException e) {
-            e.printStackTrace();
-            return null;
-        }
-    }
-
-    /**
-     * Read the number of bytes specificed from the input stream
-     */
-    protected byte[] readBytes(InputStream inStream, int startPosition, int nBytes) throws IOException {
-        byte[] bytes = new byte[nBytes];
-        int bytesRead = 0;
-        inStream.skip(startPosition);
-        while (bytesRead < nBytes) {
-            bytesRead += inStream.read(bytes, bytesRead, inStream.available());
-        }
-        return bytes;
-    }
-
-    public static int[] convertBytesToInts(byte[] bytes) {
-        try {
-            int nInts = bytes.length / (Integer.SIZE / 8);
-            int[] ints = new int[nInts];
-            DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes));
-            for (int i = 0; i < nInts; i++) {
-                if (dis.available() >= (Integer.SIZE / 8)) {
-                    ints[i] = dis.readInt();
-                }
-            }
-            dis.close();
-            return ints;
-        } catch (IOException ex) {
-            ex.printStackTrace();
-            throw new RuntimeException(ex);
-        }
-    }
-
-    public static float[] convertBytesToFloats(byte[] bytes) {
-        try {
-            int nFloats = bytes.length / (Float.SIZE / 8);
-            float[] floats = new float[nFloats];
-            DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes));
-            for (int i = 0; i < nFloats; i++) {
-                if (dis.available() >= (Float.SIZE / 8)) {
-                    floats[i] = dis.readFloat();
-                }
-            }
-            dis.close();
-            return floats;
-        } catch (IOException ex) {
-            ex.printStackTrace();
-            throw new RuntimeException(ex);
-        }
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        closeFile();
-        super.finalize();
-    }
-}
diff --git a/src/org/broad/igv/h5/ZipWriter.java b/src/org/broad/igv/h5/ZipWriter.java
deleted file mode 100644
index 26f37d5..0000000
--- a/src/org/broad/igv/h5/ZipWriter.java
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.h5;
-
-import java.io.*;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.zip.CRC32;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
-
-/**
- * @author jrobinso
- */
-public class ZipWriter extends ZipBase implements HDFWriter {
-
-    int fileId;
-    String fileName;
-    ZipOutputStream zos = null;
-    Map<String, String> attributes = new HashMap();
-
-    public ZipWriter() {
-    }
-
-    public int createFile(String name) {
-
-        try {
-
-            fileId = name.hashCode();
-            addEntity(fileId, "/");
-            this.fileName = name;
-            zos = new ZipOutputStream(new FileOutputStream(fileName));
-
-            return fileId;
-        } catch (FileNotFoundException ex) {
-            ex.printStackTrace();
-            throw new RuntimeException(ex);
-        }
-    }
-
-    private int createEntity(int locId, String name) {
-        String fullName = getFullName(locId, name);
-        int id = fullName.hashCode();
-        addEntity(id, fullName);
-        return id;
-    }
-
-    public void closeDataset(int datasetId) {
-        // No op
-    }
-
-    public void closeFile(int fileId) {
-        String entryName = "attributes";
-        this.writeMap(entryName, attributes);
-        try {
-            zos.close();
-        } catch (IOException ex) {
-            ex.printStackTrace();
-        }
-
-    }
-
-    public void closeGroup(int groupId) {
-    }
-
-    public void createAndWriteStringDataset(int locId, String dsName, String[] stringArray) {
-        String fullName = getFullName(locId, dsName);
-        this.writeStrings(fullName, stringArray);
-    }
-
-    public void createAndWriteStringDataset(int locId, String dsName, String[] stringArray,
-                                            int stringSize) {
-        createAndWriteStringDataset(locId, dsName, stringArray);
-    }
-
-    public int createDataset(int locId, String name, int typeId, long[] dims) {
-        return createEntity(locId, name);
-    }
-
-    public int createGroup(int locId, String name) {
-        return createEntity(locId, name);
-    }
-
-    public int openDataset(int fileId, String dsName) {
-        return openEntity(fileId, dsName);
-    }
-
-    public int openGroup(int locId, String name) {
-        return openEntity(locId, name);
-    }
-
-    private int openEntity(int locId, String name) {
-        // If name starts with "/" it is absolute
-        String fullName = (name.startsWith("/") ? name : getFullName(locId, name));
-        return getEntityId(fullName);
-
-    }
-
-    public int writeAttribute(int locId, String name, Object value) {
-        String entityName = getEntityName(locId);
-        String attributeName = entityName + "|" + name;
-        attributes.put(attributeName, value.toString());
-        return 1;
-    }
-
-    // Ignoring rowNumber for this test.  This will only work for single track data
-    public void writeDataValue(int datasetId, int rowNumber, float value) {
-        String dsName = getEntityName(datasetId);
-        createAndWriteFloatDataset(dsName, new float[]{value});
-    }
-
-    private void writeMap(String entryName, Map<String, String> map) {
-        try {
-            // Put strings in a byte buffer.  We need to know the total size in bytes
-            StringBuffer buff = new StringBuffer();
-            for (Map.Entry<String, String> kv : map.entrySet()) {
-                buff.append(kv.getKey() + "\t" + kv.getValue());
-                buff.append('\n');
-            }
-            byte[] bytes = buff.toString().getBytes();
-
-            createZipEntry(entryName, bytes);
-
-        } catch (IOException ex) {
-            ex.printStackTrace();
-        } finally {
-        }
-    }
-
-    private void writeStrings(String entryName, String[] strings) {
-        try {
-            // Put strings in a byte buffer.  We need to know the total size in bytes
-            StringBuffer buff = new StringBuffer();
-            for (String s : strings) {
-                buff.append(s);
-                buff.append('\n');
-            }
-            byte[] bytes = buff.toString().getBytes();
-
-            createZipEntry(entryName, bytes);
-
-        } catch (IOException ex) {
-            ex.printStackTrace();
-        } finally {
-        }
-    }
-
-    // Ignoring rowId for this test.  This will only work for single track data
-    public void writeDataRow(int datasetId, int rowId, float[] data, int length) {
-        String dsName = getEntityName(datasetId);
-
-        createAndWriteFloatDataset(dsName, (float[]) data);
-
-
-    }
-
-    /**
-     * TODO -- temporary implementation to maintain interface.  Redo with
-     * overloaded methods.
-     *
-     * @param locId
-     * @param fullName
-     * @param data
-     */
-    public void createAndWriteVectorDataset(int locId, String dsName, Object data) {
-        String fullName = getFullName(locId, dsName);
-        if (data instanceof int[]) {
-            createAndWriteIntDataset(fullName, (int[]) data);
-        } else if (data instanceof float[]) {
-            createAndWriteFloatDataset(fullName, (float[]) data);
-        } else {
-            throw new RuntimeException("Unsupported type: " + data.getClass());
-        }
-
-
-    }
-
-    private void createAndWriteIntDataset(String entryName, int[] data) {
-        try {
-
-
-            ByteArrayOutputStream bytes = new ByteArrayOutputStream(data.length * 4);
-            DataOutputStream bos = new DataOutputStream(bytes);
-            for (int i = 0; i < data.length; i++) {
-                bos.writeInt(data[i]);
-            }
-            bos.close();
-            byte[] byteArray = bytes.toByteArray();
-            createZipEntry(entryName, byteArray);
-
-        } catch (IOException ex) {
-            ex.printStackTrace();
-        }
-    }
-
-    private void createAndWriteFloatDataset(String entryName, float[] data) {
-        try {
-
-            ByteArrayOutputStream bytes = new ByteArrayOutputStream(data.length * 4);
-            DataOutputStream bos = new DataOutputStream(bytes);
-            for (int i = 0; i < data.length; i++) {
-                bos.writeFloat(data[i]);
-            }
-            bos.close();
-            byte[] byteArray = bytes.toByteArray();
-            createZipEntry(entryName, byteArray);
-
-        } catch (IOException ex) {
-            ex.printStackTrace();
-        }
-    }
-
-    private void createZipEntry(String entryName, byte[] bytes) throws IOException, IOException {
-        ZipEntry anEntry = new ZipEntry(entryName);
-        anEntry.setMethod(ZipEntry.STORED);
-        anEntry.setCompressedSize(bytes.length);
-        anEntry.setSize(bytes.length);
-
-        anEntry.setCrc(getCrc(bytes));
-        zos.putNextEntry(anEntry);
-
-        BufferedOutputStream bos = new BufferedOutputStream(zos);
-        bos.write(bytes);
-        bos.flush();
-
-        zos.closeEntry();
-    }
-
-    private long getCrc(byte[] buffer) throws IOException {
-        CRC32 crc = new CRC32();
-        crc.reset();
-        crc.update(buffer, 0, buffer.length);
-        return crc.getValue();
-
-    }
-
-    public void createAndWriteDataset(int nodeId, String dsName, float[][] data) {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-}
diff --git a/src/org/broad/igv/listener/StatusListener.java b/src/org/broad/igv/listener/StatusListener.java
index 2d3d95e..fe3d525 100644
--- a/src/org/broad/igv/listener/StatusListener.java
+++ b/src/org/broad/igv/listener/StatusListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/maf/MAFConfigurationDialog.java b/src/org/broad/igv/maf/MAFConfigurationDialog.java
index 392128e..24390b0 100644
--- a/src/org/broad/igv/maf/MAFConfigurationDialog.java
+++ b/src/org/broad/igv/maf/MAFConfigurationDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/maf/MAFLocalReader.java b/src/org/broad/igv/maf/MAFLocalReader.java
index e75d66f..d1e385b 100644
--- a/src/org/broad/igv/maf/MAFLocalReader.java
+++ b/src/org/broad/igv/maf/MAFLocalReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/maf/MAFManager.java b/src/org/broad/igv/maf/MAFManager.java
index ea87834..d9a7849 100644
--- a/src/org/broad/igv/maf/MAFManager.java
+++ b/src/org/broad/igv/maf/MAFManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -25,6 +25,7 @@ package org.broad.igv.maf;
 
 import org.apache.log4j.Logger;
 import org.broad.igv.PreferenceManager;
+import org.broad.igv.ui.WaitCursorManager;
 import org.broad.igv.util.LRUCache;
 import org.broad.igv.util.ResourceLocator;
 
@@ -59,7 +60,7 @@ public class MAFManager {
     String refId;
 
     public MAFManager(ResourceLocator locator) {
-        tileCache = new LRUCache(50);
+        tileCache = new LRUCache(this, 10);
         if (speciesNames == null) {
             loadNames();
         }
@@ -107,11 +108,16 @@ public class MAFManager {
         String key = getKey(chr, tileNo);
         MAFTile tile = tileCache.get(key);
         if (tile == null) {
-
-            int start = tileNo * tileSize;
-            int end = start + tileSize + 1;
-            tile = reader.loadTile(chr, start, end, allSpecies);
-            tileCache.put(key, tile);
+            WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
+            try {
+
+                int start = tileNo * tileSize;
+                int end = start + tileSize + 1;
+                tile = reader.loadTile(chr, start, end, allSpecies);
+                tileCache.put(key, tile);
+            } finally {
+                WaitCursorManager.removeWaitCursor(token);
+            }
 
         }
         return tile;
diff --git a/src/org/broad/igv/maf/MAFReader.java b/src/org/broad/igv/maf/MAFReader.java
index 02f1223..f65bac7 100644
--- a/src/org/broad/igv/maf/MAFReader.java
+++ b/src/org/broad/igv/maf/MAFReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/maf/MAFRemoteReader.java b/src/org/broad/igv/maf/MAFRemoteReader.java
index 7e9ebe6..9d18f6d 100644
--- a/src/org/broad/igv/maf/MAFRemoteReader.java
+++ b/src/org/broad/igv/maf/MAFRemoteReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,6 +23,7 @@
 package org.broad.igv.maf;
 
 import org.apache.log4j.Logger;
+import org.broad.igv.util.IGVHttpUtils;
 import org.broad.igv.util.ResourceLocator;
 
 import java.io.BufferedInputStream;
@@ -53,9 +54,8 @@ public class MAFRemoteReader implements MAFReader {
                             List<String> species) {
         DataInputStream is = null;
         try {
-            URL url = new URL(serverURL + "?method=maf&chr=" +
-                    chr + "&start=" + start + "&end=" + end);
-            URLConnection connection = url.openConnection();
+            URL url = new URL(serverURL + "?method=maf&chr=" + chr + "&start=" + start + "&end=" + end);
+            URLConnection connection = IGVHttpUtils.openConnection(url);
             is = new DataInputStream(new GZIPInputStream(new BufferedInputStream(connection.getInputStream())));
             MAFTile tile = codec.decode(is);
             return tile;
diff --git a/src/org/broad/igv/maf/MAFRenderer.java b/src/org/broad/igv/maf/MAFRenderer.java
index 991bfa6..76ea4d2 100644
--- a/src/org/broad/igv/maf/MAFRenderer.java
+++ b/src/org/broad/igv/maf/MAFRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -93,8 +93,7 @@ public class MAFRenderer implements Renderer {
 
     /**
      * Method description
-     *
-     * @param featureList
+
      * @param context
      * @param trackRectangle
      * @param track
@@ -149,13 +148,6 @@ public class MAFRenderer implements Renderer {
         double locScale = context.getScale();
         double origin = context.getOrigin();
 
-        int start = alignment.getStart();
-        int end = alignment.getEnd();
-
-        //byte[] read = alignment.getSequenceBases().getBytes();
-
-
-        // if ((read != null) && (read.length > 0))
         {
 
             int pY = (int) rect.getY();
@@ -169,9 +161,13 @@ public class MAFRenderer implements Renderer {
                 g.setFont(f);
             }
 
-// Loop through base pair coordinates
-            for (int loc = start; loc <
-                    end; loc++) {
+            // Loop through base pair coordinates
+            int windowStart = (int) origin - 1;
+            int windowEnd = (int) context.getEndLocation() + 1;
+            int start = Math.max(windowStart, alignment.getStart());
+            int end = Math.min(windowEnd, alignment.getEnd());
+
+            for (int loc = start; loc < end; loc++) {
 
                 int pX0 = (int) ((loc - origin) / locScale);
 
@@ -181,8 +177,7 @@ public class MAFRenderer implements Renderer {
                     continue;
                 }
 
-                boolean misMatch = Character.toUpperCase(c) != Character.toUpperCase(
-                        refBase);
+                boolean misMatch = Character.toUpperCase(c) != Character.toUpperCase(refBase);
 
                 char charToDraw = misMatch || ref == alignment ? c : '.';
 
@@ -199,8 +194,7 @@ public class MAFRenderer implements Renderer {
                     }
 
                     g.setColor(color);
-                    drawCenteredText(g, new char[]{charToDraw}, pX0, pY + 2, dX,
-                            dY - 2);
+                    drawCenteredText(g, new char[]{charToDraw}, pX0, pY + 2, dX, dY - 2);
                 } else {
                     if (color != null) {
                         g.setColor(color);
diff --git a/src/org/broad/igv/maf/MAFTile.java b/src/org/broad/igv/maf/MAFTile.java
index 83b10aa..c579f88 100644
--- a/src/org/broad/igv/maf/MAFTile.java
+++ b/src/org/broad/igv/maf/MAFTile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/maf/MAFTileCodec.java b/src/org/broad/igv/maf/MAFTileCodec.java
index 8ba8d83..0620845 100644
--- a/src/org/broad/igv/maf/MAFTileCodec.java
+++ b/src/org/broad/igv/maf/MAFTileCodec.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/maf/MAFTrack.java b/src/org/broad/igv/maf/MAFTrack.java
index c793ba6..ab86aa4 100644
--- a/src/org/broad/igv/maf/MAFTrack.java
+++ b/src/org/broad/igv/maf/MAFTrack.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -29,7 +29,7 @@ import org.broad.igv.track.RenderContext;
 import org.broad.igv.track.WindowFunction;
 import org.broad.igv.ui.FontManager;
 import org.broad.igv.ui.IGVMainFrame;
-import org.broad.igv.ui.IGVModel;
+import org.broad.igv.ui.WaitCursorManager;
 import org.broad.igv.util.ResourceLocator;
 
 import java.awt.*;
@@ -51,32 +51,21 @@ public class MAFTrack extends AbstractTrack {
     // A hack until full MAF track is implemented.
     Rectangle visibleNameRect;
 
-    /**
-     * Constructs ...
-     *
-     * @param locator
-     * @param name
-     * @param alignments
-     */
-    public MAFTrack(ResourceLocator locator, String name) {
-        super(locator, name);
+
+    public MAFTrack(ResourceLocator locator) {
+        super(locator);
         this.mgr = new MAFManager(locator);
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     @Override
     public int getHeight() {
         return GAPS_HEIGHT + (mgr.getSelectedSpecies().size() + 1) * EXPANDED_HEIGHT;
     }
 
     @Override
-    public void renderName(Graphics2D g2D, Rectangle trackRectangle, Rectangle visibleRect) {
+    public void renderName(Graphics2D g2D, Rectangle trackRectangle, Rectangle visibleRectangle) {
 
-        this.visibleNameRect = visibleRect;
+        this.visibleNameRect = trackRectangle;
         if (isSelected()) {
             g2D.setBackground(Color.LIGHT_GRAY);
         } else {
@@ -115,8 +104,13 @@ public class MAFTrack extends AbstractTrack {
             if (name == null) {
                 name = sp;
             }
-            GraphicUtils.drawVerticallyCenteredText(name, margin, rect, g2D, true);
-            rect.y += rect.height;
+
+            if (visibleRectangle.intersects(rect)) {
+
+                GraphicUtils.drawVerticallyCenteredText(name, margin, rect, g2D, true);
+            }
+            
+            rect.y += rect.height; 
         }
 
     }
@@ -144,7 +138,6 @@ public class MAFTrack extends AbstractTrack {
 
         double origin = context.getOrigin();
         String chr = context.getChr();
-        String genome = IGVModel.getInstance().getViewContext().getGenomeId();
 
         int start = (int) origin;
         int end = (int) (origin + rect.width * locScale) + 1;
@@ -155,11 +148,15 @@ public class MAFTrack extends AbstractTrack {
         //}
 
 // Get tiles
-        MAFTile[] tiles = mgr.getTiles(chr, start, end);
-        for (MAFTile tile : tiles) {
-            // render tile
-            if (tile != null) {
-                renderTile(context, rect, tile);
+        MAFTile[] tiles =  mgr.getTiles(chr, start, end);
+
+
+        if (tiles != null) {
+            for (MAFTile tile : tiles) {
+                // render tile
+                if (tile != null) {
+                    renderTile(context, rect, tile);
+                }
             }
         }
 
diff --git a/src/org/broad/igv/maf/MAFUtils.java b/src/org/broad/igv/maf/MAFUtils.java
index c5529a0..386f652 100644
--- a/src/org/broad/igv/maf/MAFUtils.java
+++ b/src/org/broad/igv/maf/MAFUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/maf/ReadTest.java b/src/org/broad/igv/maf/ReadTest.java
index 49fd822..7164904 100644
--- a/src/org/broad/igv/maf/ReadTest.java
+++ b/src/org/broad/igv/maf/ReadTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/maf/VirusMafTest.java b/src/org/broad/igv/maf/VirusMafTest.java
index 9820f30..946b694 100644
--- a/src/org/broad/igv/maf/VirusMafTest.java
+++ b/src/org/broad/igv/maf/VirusMafTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/maf/conservation/OmegaDataSource.java b/src/org/broad/igv/maf/conservation/OmegaDataSource.java
index 5be5f9c..0b5fcd2 100644
--- a/src/org/broad/igv/maf/conservation/OmegaDataSource.java
+++ b/src/org/broad/igv/maf/conservation/OmegaDataSource.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -41,14 +41,18 @@ public class OmegaDataSource extends AbstractDataSource implements DataSource {
 
     File rootDir = new File("/Volumes/igv/annotations/hg18/conservation/omega/12mer");
 
-    public double getDataMax(String chr) {
+    public double getDataMax() {
         return 1;
     }
 
-    public double getDataMin(String chr) {
+    public double getDataMin() {
         return 0;
     }
 
+    public int getLongestFeature(String chr) {
+        return 1;
+    }
+
     /**
      * public List<LocusScore> getSummaryScoresForRange(String chr, int startLocation, int endLocation,
      * int zoom) {
@@ -110,10 +114,10 @@ public class OmegaDataSource extends AbstractDataSource implements DataSource {
     }
 
 
-    LRUCache<String, DataTile> tileCache = new LRUCache(3);
+    LRUCache<String, DataTile> tileCache = new LRUCache(this, 3);
 
     @Override
-    public DataTile getRawData(String chr, int startLocation, int endLocation, int zoom) {
+    public DataTile getRawData(String chr, int startLocation, int endLocation) {
 
         // harcode to egfr
         startLocation = 55240000;
@@ -161,11 +165,6 @@ public class OmegaDataSource extends AbstractDataSource implements DataSource {
 
     }
 
-    @Override
-    public double getMedian(int zoom, String chr) {
-        return 1;
-    }
-
     /*
      *         private int[] startLocations;
     private int[] endLocations;
diff --git a/src/org/broad/igv/maf/conservation/OmegaFileParser.java b/src/org/broad/igv/maf/conservation/OmegaFileParser.java
index 894a5cb..70a5a9b 100644
--- a/src/org/broad/igv/maf/conservation/OmegaFileParser.java
+++ b/src/org/broad/igv/maf/conservation/OmegaFileParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,9 +23,9 @@
 package org.broad.igv.maf.conservation;
 
 import org.broad.igv.feature.LocusScore;
-import org.broad.igv.util.ParsingUtils;
 import org.broad.igv.track.WindowFunction;
 import org.broad.igv.util.AsciiLineReader;
+import org.broad.igv.util.ParsingUtils;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -101,6 +101,10 @@ public class OmegaFileParser {
             this.score = score;
         }
 
+        public String getChr() {
+            return null;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
         public int getStart() {
             return position;
         }
diff --git a/src/org/broad/igv/maf/conservation/OmegaTrack.java b/src/org/broad/igv/maf/conservation/OmegaTrack.java
index ad4e898..21ba686 100644
--- a/src/org/broad/igv/maf/conservation/OmegaTrack.java
+++ b/src/org/broad/igv/maf/conservation/OmegaTrack.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -34,8 +34,8 @@ import java.awt.*;
  */
 public class OmegaTrack extends DataSourceTrack {
 
-    public OmegaTrack(ResourceLocator locator, String name, DataSource ds) {
-        super(locator, name, ds);
+    public OmegaTrack(ResourceLocator locator, DataSource ds) {
+        super(locator, locator.getPath(), locator.getFileName(), ds);
     }
 
     @Override
diff --git a/src/org/broad/igv/maf/species.properties b/src/org/broad/igv/maf/species.properties
index 1ca2c76..cadc8de 100644
--- a/src/org/broad/igv/maf/species.properties
+++ b/src/org/broad/igv/maf/species.properties
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+# Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
 # All Rights Reserved.
 #
 # This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/main/BatchRunner.java b/src/org/broad/igv/main/BatchRunner.java
new file mode 100644
index 0000000..4720c7b
--- /dev/null
+++ b/src/org/broad/igv/main/BatchRunner.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.main;
+
+import org.apache.log4j.Logger;
+import org.broad.igv.Globals;
+import org.broad.igv.exceptions.DataLoadException;
+import org.broad.igv.ui.WaitCursorManager;
+import org.broad.igv.util.NamedRunnable;
+import org.broad.igv.util.ParsingUtils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+
+public class BatchRunner implements NamedRunnable {
+    private static Logger log = Logger.getLogger(BatchRunner.class);
+
+    String inputFile;
+
+    public BatchRunner(final String inputFile) {
+
+        this.inputFile = inputFile;
+
+    }
+
+    public String getName() {
+        return "batchExecution";  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public void run() {
+        String inLine;
+        CommandExecutor cmdExe = new CommandExecutor();
+        Globals.setSuppress(true);
+        Globals.batch = true;
+
+       WaitCursorManager.CursorToken cursorToken = null;
+        BufferedReader reader = null;
+        try {
+            cursorToken = WaitCursorManager.showWaitCursor();
+            reader = ParsingUtils.openBufferedReader(inputFile);
+
+            while ((inLine = reader.readLine()) != null) {
+                if (!(inLine.startsWith("#") || inLine.startsWith("//"))) {
+                    log.info("Executing Command: " + inLine);
+                    cmdExe.execute(inLine);
+                }
+            }
+
+
+        } catch (IOException ioe) {
+            throw new DataLoadException(ioe.getMessage(), inputFile);
+        } finally {
+            Globals.setSuppress(false);
+            Globals.batch = false;
+            if (cursorToken != null) WaitCursorManager.removeWaitCursor(cursorToken);
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+}
diff --git a/src/org/broad/igv/main/CommandExecutor.java b/src/org/broad/igv/main/CommandExecutor.java
new file mode 100644
index 0000000..1d2513c
--- /dev/null
+++ b/src/org/broad/igv/main/CommandExecutor.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.broad.igv.main;
+
+import org.apache.log4j.Logger;
+import org.broad.igv.feature.Locus;
+import org.broad.igv.sam.AlignmentTrack;
+import org.broad.igv.track.RegionScoreType;
+import org.broad.igv.track.TrackManager;
+import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.RegionOfInterest;
+import org.broad.igv.ui.WaitCursorManager;
+import org.broad.igv.util.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public class CommandExecutor {
+
+    private static Logger log = Logger.getLogger(CommandExecutor.class);
+
+
+    private List<String> getArgs(String[] tokens) {
+        List<String> args = new ArrayList(tokens.length);
+        for (String s : tokens) {
+            if (s.trim().length() > 0) {
+                args.add(s.trim());
+            }
+        }
+        return args;
+    }
+
+    public String execute(String command) {
+
+        List<String> args = getArgs(StringUtils.breakQuotedString(command, ' ').toArray(new String[]{}));
+
+        String result = "OK";
+        final IGVMainFrame mainFrame = IGVMainFrame.getInstance();
+
+        System.out.println();
+        log.debug("Executing: " + command);
+        try {
+            if (args.size() > 0) {
+
+                String cmd = args.get(0).toLowerCase();
+                String param1 = args.size() > 1 ? args.get(1) : null;
+                String param2 = args.size() > 2 ? args.get(2) : null;
+                String param3 = args.size() > 3 ? args.get(3) : null;
+
+                if (cmd.equals("echo")) {
+                    result = cmd;
+                } else if (cmd.equals("goto")) {
+                    result = goto1(param1);
+                } else if (cmd.equals("snapshotdirectory")) {
+                    result = setSnapshotDirectory(param1);
+
+                } else if (cmd.equals("snapshot")) {
+                    String filename = param1;
+                    MacroSnapshotAction.doSnapshot(filename);
+
+                } else if ((cmd.equals("loadfile") || cmd.equals("load")) && param1 != null) {
+                    result = load(param1);
+                } else if (cmd.equals("hget") && args.size() > 3) {
+                    result = hget(param1, param2, param3);
+                } else if (cmd.equals("genome") && args.size() > 1) {
+                    result = genome(param1);
+                } else if (cmd.equals("new") || cmd.equals("reset") || cmd.equals("clear")) {
+                    mainFrame.createNewSession(null);
+                } else if (cmd.equals("sort")) {
+                    sort(param1, param2, param3);
+                } else if (cmd.equals("collapse")) {
+                    String trackName = param1 == null ? null : param1.replace("\"", "").replace("'", "");
+                    collapse(trackName);
+                } else if (cmd.equals("expand")) {
+                    String trackName = param1 == null ? null : param1.replace("\"", "").replace("'", "");
+                    expand(trackName);
+                } else if (cmd.equals("tweakdivider")) {
+                    IGVMainFrame.getInstance().tweakPanelDivider();
+                } else if (cmd.equals("exit")) {
+                    System.exit(0);
+                } else {
+                    log.error("UNKOWN COMMAND: " + command);
+                    return "UNKOWN COMMAND: " + command;
+                }
+            } else {
+                return "Empty command string";
+            }
+            IGVMainFrame.getInstance().doRefresh();
+
+            if (RuntimeUtils.getAvailableMemoryFraction() < 0.5) {
+                log.debug("Clearing caches");
+                LRUCache.clearCaches();
+            }
+            log.debug("Finished execution: " + command + "  sleeping ....");
+            Thread.sleep(2000);
+            log.debug("Finished sleeping");
+
+        } catch (Exception e) {
+            log.error("Could not Parse Command", e);
+            return "ERROR Could not Parse Command: " + e.toString();
+        }
+        log.info(result);
+
+        return result;
+    }
+
+    private String genome(String param1) {
+        if (param1 == null) {
+            return "ERROR missing genome parameter";
+        }
+        String result;
+        String genomeID = param1;
+        IGVMainFrame.getInstance().selectGenomeFromList(genomeID);
+        result = "OK";
+        return result;
+    }
+
+    private String hget(String param1, String param2, String param3) {
+        String result;
+        String fileString = param1;
+        String locusString = param2;
+        String mergeValue = param3;
+        boolean merge = mergeValue != null && mergeValue.equalsIgnoreCase("true");
+        result = loadFiles(fileString, locusString, merge);
+        return result;
+    }
+
+    private String load(String param1) {
+        if (param1 == null) {
+            return "ERROR: missing path parameter";
+        }
+        String fileString = param1.replace("\"", "").replace("'", "");
+        return loadFiles(fileString, null, true);
+    }
+
+    private String setSnapshotDirectory(String param1) {
+        if (param1 == null) {
+            return "ERROR: missing directory parameter";
+        }
+
+        String result;
+        File parentDir = new File(param1);
+        if (parentDir.exists()) {
+            MacroSnapshotAction.setOutputDirectory(param1);
+            result = "OK";
+        } else {
+            result = "ERROR: directory: " + param1 + " does not exist";
+        }
+        return result;
+    }
+
+    private String goto1(String param1) {
+        if (param1 == null) {
+            return "ERROR: missing locus parameter";
+        }
+        String locus = param1;
+        IGVMainFrame.getInstance().goToLocus(locus);
+        return "OK";
+    }
+
+    private void collapse(String trackName) {
+        if (trackName == null) {
+            IGVMainFrame.getInstance().getTrackManager().collapseTracks();
+        } else {
+            IGVMainFrame.getInstance().getTrackManager().collapseTrack(trackName);
+        }
+        IGVMainFrame.getInstance().repaintDataPanels();
+    }
+
+
+    private void expand(String trackName) {
+        if (trackName == null) {
+            IGVMainFrame.getInstance().getTrackManager().expandTracks();
+        } else {
+            IGVMainFrame.getInstance().getTrackManager().expandTrack(trackName);
+        }
+        IGVMainFrame.getInstance().repaintDataPanels();
+    }
+
+
+    private void sort(String sortArg, String locusString, String param3) {
+        TrackManager tm = IGVMainFrame.getInstance().getTrackManager();
+        RegionScoreType regionSortOption = getRegionSortOption(sortArg);
+        if (regionSortOption != null) {
+            RegionOfInterest roi = null;
+            if (locusString != null) {
+                Locus locus = new Locus(locusString);
+                if (locus.isValid()) {
+                    roi = new RegionOfInterest(locus.getChr(), locus.getStart(), locus.getEnd(), "");
+                }
+            }
+            tm.sortByRegionScore(roi, regionSortOption);
+
+        } else {
+            Double location = null;
+            if (param3 != null) {
+                try {
+                    location = new Double(param3);
+                }
+                catch (NumberFormatException e) {
+                    log.info("Unexpected sort location argument: " + param3);
+                }
+            }
+            if (location == null) {
+                tm.sortAlignmentTracks(getAlignmentSortOption(sortArg));
+            } else {
+                tm.sortAlignmentTracks(getAlignmentSortOption(sortArg), location);
+            }
+        }
+        IGVMainFrame.getInstance().repaintDataPanels();
+    }
+
+    private String loadFiles(final String fileString, final String locus, final boolean merge) {
+
+        log.debug("Run load files");
+        WaitCursorManager.CursorToken token = null;
+        try {
+            token = WaitCursorManager.showWaitCursor();
+
+            String[] files = fileString.split(",");
+            List<ResourceLocator> fileLocators = new ArrayList<ResourceLocator>();
+            List<String> sessionPaths = new ArrayList<String>();
+
+            if (!merge) {
+                IGVMainFrame.getInstance().createNewSession(null);
+            }
+
+            for (String f : files) {
+                if (f.endsWith(".xml")) {
+                    sessionPaths.add(f);
+                } else {
+                    ResourceLocator rl = new ResourceLocator(f);
+                    fileLocators.add(rl);
+                }
+            }
+
+            for (String sessionPath : sessionPaths) {
+                InputStream is = null;
+                try {
+                    is = ParsingUtils.openInputStream(new ResourceLocator(sessionPath));
+                    IGVMainFrame.getInstance().doRestoreSession(is, sessionPath, locus, merge);
+                }
+                catch (IOException e) {
+                    log.error(e.getMessage(), e);
+                }
+                finally {
+                    if (is != null) {
+                        try {
+                            is.close();
+                        } catch (IOException e) {
+                            log.error(e.getMessage(), e);
+                        }
+                    }
+                }
+            }
+
+            //TODO Find a better way to get the message back up to the socket.
+            IGVMainFrame.getInstance().getTrackManager().loadResources(fileLocators);
+
+            if (locus != null) {
+                IGVMainFrame.getInstance().goToLocus(locus);
+            }
+        }
+        finally {
+            WaitCursorManager.removeWaitCursor(token);
+        }
+
+
+        return "OK";
+    }
+
+    private static RegionScoreType getRegionSortOption(String str) {
+        if (str == null) return null;
+        String option = str.toUpperCase();
+        try {
+            return RegionScoreType.valueOf(option);
+        }
+        catch (Exception e) {
+            return null;
+        }
+    }
+
+
+    //START, STRAND, NUCLEOTIDE, QUALITY, SAMPLE, READ_GROUP
+    private static AlignmentTrack.SortOption getAlignmentSortOption(String str) {
+        String option = str.toLowerCase();
+        if (str == null || option.equals("base")) {
+            return AlignmentTrack.SortOption.NUCELOTIDE;
+        } else if (option.equals("strand")) {
+            return AlignmentTrack.SortOption.STRAND;
+
+        } else if (option.equals("start") || option.equals("position")) {
+            return AlignmentTrack.SortOption.START;
+
+        } else if (option.equals("quality")) {
+            return AlignmentTrack.SortOption.QUALITY;
+
+        } else if (option.equals("sample")) {
+            return AlignmentTrack.SortOption.SAMPLE;
+
+        } else if (option.equals("readGroup") || option.equals("read_group")) {
+            return AlignmentTrack.SortOption.READ_GROUP;
+        }
+        return AlignmentTrack.SortOption.NUCELOTIDE;
+    }
+}
diff --git a/src/org/broad/igv/main/CommandListener.java b/src/org/broad/igv/main/CommandListener.java
index 9e80286..cc84f62 100644
--- a/src/org/broad/igv/main/CommandListener.java
+++ b/src/org/broad/igv/main/CommandListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -18,22 +18,19 @@
 package org.broad.igv.main;
 
 import org.apache.log4j.Logger;
+import org.broad.igv.Globals;
 import org.broad.igv.PreferenceManager;
-import org.broad.igv.util.ParsingUtils;
-import org.broad.igv.sam.AlignmentTrack;
-import org.broad.igv.track.TrackManager;
 import org.broad.igv.ui.IGVMainFrame;
-import org.broad.igv.ui.LongRunningTask;
-import org.broad.igv.util.MacroSnapshotAction;
-import org.broad.igv.util.ResourceLocator;
+import org.broad.igv.ui.WaitCursorManager;
 
-import java.io.*;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.net.URLDecoder;
-import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
 public class CommandListener implements Runnable {
@@ -59,6 +56,7 @@ public class CommandListener implements Runnable {
     public void run() {
 
         int port = PreferenceManager.getInstance().getPortNumber();
+        CommandExecutor cmdExe = new CommandExecutor();
 
         ServerSocket serverSocket = null;
         try {
@@ -68,37 +66,50 @@ public class CommandListener implements Runnable {
             while (halt == false) {
                 Socket clientSocket = null;
                 try {
-                    System.out.println("Listening on port: " + port);
+//                    System.out.println("Listening on port: " + port);
                     clientSocket = serverSocket.accept();
 
                     PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
                     BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
 
                     String inputLine;
+
+
                     while ((inputLine = in.readLine()) != null) {
-                        String cmd = inputLine;
-                        if (halt == true) {
+                        Globals.batch = true;
+                        Globals.setSuppress(true);
+                        WaitCursorManager.CursorToken cursorToken = null;
+                        try {
+                            String cmd = inputLine;
+                            if (halt == true) {
+                                if (cmd.startsWith("GET")) {
+                                    sendHTTPResponse(out, "ERROR IGV port is closed");
+                                } else {
+                                    out.println("ERROR IGV port is closed");
+                                }
+                                break;
+                            }
                             if (cmd.startsWith("GET")) {
-                                sendHTTPResponse(out, "ERROR IGV port is closed");
+                                String result = processGet(cmd, in, cmdExe);
+                                sendHTTPResponse(out, result);
+                                clientSocket.close();
+
+                                clientSocket = serverSocket.accept();
+                                out = new PrintWriter(clientSocket.getOutputStream(), true);
+                                in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
+
                             } else {
-                                out.println("ERROR IGV port is closed");
+                                out.println(cmdExe.execute(inputLine));
                             }
-                            break;
+                        } finally {
+                            Globals.setSuppress(false);
+                            Globals.batch = false;
+                            if (cursorToken != null) WaitCursorManager.removeWaitCursor(cursorToken);
                         }
-                        if (cmd.startsWith("GET")) {
-                            String result = processGet(cmd, in);
-                            sendHTTPResponse(out, result);
-                            clientSocket.close();
 
-                            clientSocket = serverSocket.accept();
-                            out = new PrintWriter(clientSocket.getOutputStream(), true);
-                            in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
+                    }
 
-                        } else {
-                            out.println(parseCommand(inputLine));
-                        }
 
-                    }
                     out.close();
                     in.close();
                     clientSocket.close();
@@ -106,12 +117,13 @@ public class CommandListener implements Runnable {
                     log.error("Accept failed.", e);
                 } finally {
                     clientSocket.close();
+                    Globals.setSuppress(false);
                 }
 
             }
 
         } catch (java.net.BindException e) {
-            // Expected, nothing to do
+            log.error(e);
         } catch (Exception e) {
             log.error("Could not listen on port: " + port, e);
         } finally {
@@ -126,150 +138,15 @@ public class CommandListener implements Runnable {
     }
 
     private void sendHTTPResponse(PrintWriter out, String result) {
-        out.println("HTTP/1.0 200 OK");
+        out.println("HTTP/1.0 204 OK");
         out.println(" Server: IGV");
         out.println("Connection: close");
-        out.println("Content-Type: text/html; charset=UTF-8");
         out.println();
         out.println(result);
         out.println();
         out.close();
     }
 
-    private String parseCommand(String command) {
-        String[] tokens = command.split(" ");
-        try {
-            if (tokens.length > 0) {
-                String cmd = tokens[0].trim().toLowerCase();
-                if (cmd.equals("echo")) {
-                    return cmd;
-                } else if (cmd.equals("goto") && tokens.length > 1) {
-                    String locus = tokens.length > 1 ? tokens[1] : null;
-                    IGVMainFrame.getInstance().goToLocus(locus);
-                    return "OK";
-                } else if (cmd.equals("snapshotdirectory") && tokens.length > 1) {
-                    MacroSnapshotAction.setOutputDirectory(tokens[1].trim());
-                    return "OK";
-                } else if (cmd.equals("snapshot")) {
-                    String filename = null;
-                    if (tokens.length > 1) {
-                        filename = tokens[1].trim();
-                    }
-                    MacroSnapshotAction.doSnapshot(filename);
-                    return "OK";
-                } else if (cmd.equals("load") && tokens.length > 1) {
-                    String fileString = tokens[1];
-                    loadFiles(fileString, null, true);
-                    return "OK";
-                } else if (cmd.equals("genome") && tokens.length > 1) {
-                    String genomeID = tokens[1].trim();
-                    IGVMainFrame.getInstance().selectGenomeFromList(genomeID);
-                    return "OK";
-                } else if (cmd.equals("new")) {
-                    IGVMainFrame.getInstance().reset();
-                    return "OK";
-                } else if (cmd.equals("sort")) {
-                    String sortArg = tokens.length > 1 ? tokens[1].toLowerCase() : null;
-                    TrackManager.getInstance().sortAlignmentTracks(getSortOption(sortArg));
-                    IGVMainFrame.getInstance().repaintDataPanels();
-                    return "OK";
-
-                } else if (cmd.equals("collapse")) {
-                    TrackManager.getInstance().collapseTracks();
-                    IGVMainFrame.getInstance().repaintDataPanels();
-                    return "OK";
-                }
-
-            }
-            return "UNKOWN COMMAND: " + command;
-        } catch (Exception e) {
-            return "ERROR " + e.getMessage();
-        }
-    }
-
-    //START, STRAND, NUCELOTIDE, QUALITY, SAMPLE, READ_GROUP
-    private AlignmentTrack.SortOption getSortOption(String str) {
-        if (str == null || str.equals("base")) {
-            return AlignmentTrack.SortOption.NUCELOTIDE;
-        } else if (str.equals("strand")) {
-            return AlignmentTrack.SortOption.STRAND;
-
-        } else if (str.equals("start") || str.equals("position")) {
-            return AlignmentTrack.SortOption.START;
-
-        } else if (str.equals("quality")) {
-            return AlignmentTrack.SortOption.QUALITY;
-
-        } else if (str.equals("sample")) {
-            return AlignmentTrack.SortOption.SAMPLE;
-
-        } else if (str.equals("readGroup") || str.equals("read_group")) {
-            return AlignmentTrack.SortOption.READ_GROUP;
-        }
-        return AlignmentTrack.SortOption.NUCELOTIDE;
-    }
-
-    private void loadFiles(final String fileString, final String locus, final boolean merge) {
-        Runnable runnable = new Runnable() {
-
-
-            public void run() {
-
-                log.debug("Run load files");
-
-                String[] files = fileString.split(",");
-
-                List<ResourceLocator> fileLocators = new ArrayList();
-                List<String> sessionPaths = new ArrayList();
-
-                if (merge == false) {
-                    IGVMainFrame.getInstance().reset();
-                }
-
-                for (String f : files) {
-                    if (f.endsWith(".xml")) {
-                        sessionPaths.add(f);
-                    } else {
-                        ResourceLocator rl = new ResourceLocator(f);
-                        fileLocators.add(rl);
-                    }
-                }
-
-                for (String session : sessionPaths) {
-                    InputStream is = null;
-                    try {
-                        is = ParsingUtils.openInputStream(new ResourceLocator(session));
-                        IGVMainFrame.getInstance().doRestoreSession(is, session, locus, merge);
-                    }
-                    catch (IOException e) {
-
-                    }
-                    finally {
-                        if (is != null) {
-                            try {
-                                is.close();
-                            } catch (IOException e) {
-                                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
-                            }
-                        }
-                    }
-                }
-
-                TrackManager.getInstance().loadResources(fileLocators);
-
-                if (locus != null) {
-                    IGVMainFrame.getInstance().goToLocus(locus);
-                }
-            }
-        };
-        // Note: the "get()" call blocks until the task is compelete
-        try {
-            LongRunningTask.submit(runnable).get();
-        } catch (Exception e) {
-            log.error("Error running port command", e);
-        }
-    }
-
     /**
      * Process an http get request.
      *
@@ -278,44 +155,50 @@ public class CommandListener implements Runnable {
      * @return
      * @throws IOException
      */
-    String processGet(String line, BufferedReader reader) throws IOException {
+
+    private String processGet(String line, BufferedReader reader, CommandExecutor cmdExe) throws IOException {
 
         String nextLine = URLDecoder.decode(line);
-        String result = "";
+        String result = "OK";
 
         String[] tokens = nextLine.split(" ");
         if (tokens.length < 2) {
-            result = "ERROR unexpected command line: " + line;
+            return "ERROR unexpected command line: " + line;
         } else {
             String[] parts = tokens[1].split("\\?");
             if (parts.length < 2) {
-                result = "ERROR unexpected command line: " + line;
+                return ("ERROR unexpected command line: " + line);
             } else {
                 String command = parts[0];
                 Map<String, String> params = parseParameters(parts[1]);
-                if (command.equals("/load")) {
+                final IGVMainFrame mainFrame = IGVMainFrame.getInstance();
 
-                    String mergeValue = params.get("merge");
-                    boolean merge = mergeValue != null && mergeValue.equalsIgnoreCase("true");
-
-                    String locus = params.get("locus");
+                // Trick to force window to front, the setAlwaysOnTop works on a Mac,  toFront() does nothing.
+                mainFrame.toFront();
+                mainFrame.setAlwaysOnTop(true);
+                mainFrame.setAlwaysOnTop(false);
 
+                if (command.equals("/load")) {
                     if (params.containsKey("file")) {
-                        loadFiles(params.get("file"), locus, merge);
-                        result = "OK";
-                    } else {
-                        result = "ERROR Parameter \"file\" is required";
-                    }
+                        String genomeID = params.get("genome");
+                        String mergeValue = params.get("merge");
+                        String locus = params.get("locus");
+                        if (genomeID != null) {
+                            mainFrame.selectGenomeFromList(genomeID);
+                        }
+
+                        // Default for merge is "true"
+                        boolean merge = mergeValue == null ? true : mergeValue.equalsIgnoreCase("true");
 
-                    String genomeID = params.get("genome");
-                    if (genomeID != null) {
-                        IGVMainFrame.getInstance().selectGenomeFromList(genomeID);
+                        result = cmdExe.execute("hget " + params.get("file") + " " + locus + " " + merge);
+                    } else {
+                        return ("ERROR Parameter \"file\" is required");
                     }
                 } else if (command.equals("/reload") || command.equals("/goto")) {
                     String locus = params.get("locus");
-                    IGVMainFrame.getInstance().goToLocus(locus);
+                    mainFrame.goToLocus(locus);
                 } else {
-                    result = "ERROR Unknown command: " + command;
+                    return ("ERROR Unknown command: " + command);
                 }
             }
         }
diff --git a/src/org/broad/igv/main/Exec.java b/src/org/broad/igv/main/Exec.java
new file mode 100644
index 0000000..c8a3f8e
--- /dev/null
+++ b/src/org/broad/igv/main/Exec.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.main;
+
+import java.io.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: Apr 29, 2010
+ * Time: 1:29:50 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class Exec {
+
+    public static void main(String args[]) throws IOException {
+
+        Process p = Runtime.getRuntime().exec("cat");
+
+        OutputStream os = p.getOutputStream();
+
+        PrintWriter pw = new PrintWriter(new OutputStreamWriter(os));
+        pw.println("abcd") ;
+        pw.flush();
+        os.close();
+
+        InputStream is = p.getInputStream();
+        
+        int b;
+        while (((b = is.read())) > 0) {
+            System.out.print((char) b);
+        }
+    }
+}
diff --git a/src/org/broad/igv/main/KnockKnockClient.java b/src/org/broad/igv/main/KnockKnockClient.java
deleted file mode 100644
index c0b553c..0000000
--- a/src/org/broad/igv/main/KnockKnockClient.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.main;
-
-/*
-The Dynamic and/or Private Ports are those from 49152 through 65535
- */
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.net.Socket;
-import java.net.UnknownHostException;
-
-public class KnockKnockClient {
-
-    public static void main(String[] args) throws IOException {
-
-        Socket kkSocket = null;
-        PrintWriter out = null;
-        BufferedReader in = null;
-
-        try {
-            kkSocket = new Socket("127.0.0.1", 4444);
-            out = new PrintWriter(kkSocket.getOutputStream(), true);
-            in = new BufferedReader(new InputStreamReader(kkSocket.getInputStream()));
-        } catch (UnknownHostException e) {
-            System.err.println("Don't know about host: taranis.");
-            System.exit(1);
-        } catch (IOException e) {
-            e.printStackTrace();
-            System.err.println("Couldn't get I/O for the connection to: taranis.");
-            System.exit(1);
-        }
-
-        BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
-        String fromServer;
-        String fromUser;
-
-        out.println("Hello");
-
-        while ((fromServer = in.readLine()) != null) {
-            System.out.println("Server: " + fromServer);
-            if (fromServer.equals("Bye.")) {
-                break;
-            }
-
-            fromUser = stdIn.readLine();
-            if (fromUser != null) {
-                System.out.println("Client: " + fromUser);
-                out.println(fromUser);
-            }
-        }
-
-        out.close();
-        in.close();
-        stdIn.close();
-        kkSocket.close();
-    }
-}
diff --git a/src/org/broad/igv/main/TestClient.java b/src/org/broad/igv/main/TestClient.java
index c8ff4cc..acb8ee9 100644
--- a/src/org/broad/igv/main/TestClient.java
+++ b/src/org/broad/igv/main/TestClient.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -25,12 +25,119 @@ import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.net.Socket;
 import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
 
 public class TestClient {
 
     static private String sessionURL = "http://www.broadinstitute.org/mmgp/textReader/IGV/mmrc_session.xml";
+    static private String fileURL = "http://www.broadinstitute.org/igvdata/cshcourse/rwpe.washu.merged.bam";
 
-    public static void main(String[] args) throws IOException {
+    public static void main(String args[]) throws IOException {
+        Socket socket = null;
+        PrintWriter out = null;
+        BufferedReader in = null;
+        try {
+            socket = new Socket("127.0.0.1", 60151);
+            out = new PrintWriter(socket.getOutputStream(), true);
+            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            testLoopBAM(out, in);
+        } catch (UnknownHostException e) {
+            System.err.println("Unknown host exception: " + e.getMessage());
+            System.exit(1);
+        } catch (IOException e) {
+            e.printStackTrace();
+            System.err.println("Couldn't get I/O for " + "the connection to IGV");
+            System.exit(1);
+        } finally {
+            in.close();
+            out.close();
+            socket.close();
+        }
+    }
+
+
+    private static void testLoopBAM(PrintWriter out, BufferedReader in) throws IOException {
+
+        String fileURL = "http://www.broadinstitute.org/igvdata/1KG/freeze5_merged/low_coverage_YRI.13.bam";
+        String chr = "chr13";
+        int chrLength = 113000000;
+
+        String cmd = "snapshotDirectory /Users/jrobinso/tmp";
+        out.println(cmd);
+        String response = in.readLine();
+        System.out.println(cmd + " " + response);
+
+        cmd = "load " + fileURL;
+        out.println(cmd);
+        response = in.readLine();
+        System.out.println(cmd + " " + response);
+
+        for (int i = 0; i < 200; i++) {
+
+            int start = 1 + (int) (Math.random() * (chrLength - 10000));
+            int end = start + 8000;
+            String locusString = chr + ":" + start + "-" + end;
+            cmd = "goto " + locusString;
+            out.println(cmd);
+            response = in.readLine();
+            System.out.println("" + i + " " + cmd + " " + response);
+
+            cmd = "snapshot test_" + i + ".png";
+            out.println(cmd);
+            response = in.readLine();
+            System.out.println("" + i + " " + cmd + " " + response);
+        }
+
+    }
+
+
+    /**
+     * This test repeatedly creates new sessions and loads a BAM file.  It reveals a slow memory leak, still
+     * unresolved.
+     *
+     * @param out
+     * @param in
+     * @throws IOException
+     */
+    private static void testLoopSessions(PrintWriter out, BufferedReader in) throws IOException {
+
+        List<String> commands = new ArrayList();
+        commands.add("snapshotDirectory /Users/jrobinso/tmp");
+        commands.add("new");
+        commands.add("load " + fileURL);
+        commands.add("goto chr1:11,554,759-11,555,759");
+        //commands.add("snapshot");
+        //commands.add("collapse");
+        //commands.add("snapshot");
+
+        for (int i = 0; i < 100; i++) {
+
+            for (String cmd : commands) {
+                out.println(cmd);
+                String response = in.readLine();
+                System.out.println("" + i + " " + cmd + " " + response);
+            }
+            String cmd = "snapshot test_" + i + ".png";
+            out.println(cmd);
+            String response = in.readLine();
+            System.out.println("" + i + " " + cmd + " " + response);
+        }
+
+    }
+
+    private static void testFileWithSpaces(PrintWriter out, BufferedReader in) throws IOException {
+        String response;
+        String command = "load \"/Users/jrobinso/projects/Version_1.5_rc2/test/data/gct/file with spaces.gct\"";
+        System.out.println("asking igv to " + command);
+        out.println(command);
+        System.out.println("waiting for response");
+        response = in.readLine();
+        System.out.println(response);
+    }
+
+
+    public static void test2(String[] args) throws IOException {
 
         Socket socket = null;
         PrintWriter out = null;
@@ -88,12 +195,12 @@ public class TestClient {
         response = in.readLine();
         System.out.println(response);
 
-        out.println("goto chr1:65,827,301");
+        out.println("goto chr7:41,790,257-68,534,649");
         //out.println("goto chr1:65,839,697");
         response = in.readLine();
         System.out.println(response);
 
-        out.println("sort");
+        out.println("sort amplification chr7:55,096,094-55,200,563");
         response = in.readLine();
         System.out.println(response);
 
diff --git a/src/org/broad/igv/preprocess/old/AbstractProcessor.java b/src/org/broad/igv/preprocess/old/AbstractProcessor.java
deleted file mode 100644
index 6650cc5..0000000
--- a/src/org/broad/igv/preprocess/old/AbstractProcessor.java
+++ /dev/null
@@ -1,914 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-/*
- * To change this template, choose Tools | Templates
- * and openFile the template in the editor.
- */
-package org.broad.igv.preprocess.old;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import cern.colt.list.DoubleArrayList;
-import cern.jet.stat.quantile.DoubleQuantileFinder;
-import cern.jet.stat.quantile.QuantileFinderFactory;
-import ncsa.hdf.hdf5lib.HDF5Constants;
-import org.apache.log4j.Logger;
-import static org.broad.igv.IGVConstants.CHR_ALL;
-import org.broad.igv.data.DataStatistics;
-import org.broad.igv.data.Dataset;
-import org.broad.igv.data.GenomeSummaryData;
-import org.broad.igv.data.ProcessingUtils;
-import org.broad.igv.feature.Chromosome;
-import org.broad.igv.feature.Genome;
-import org.broad.igv.feature.GenomeManager;
-import org.broad.igv.h5.HDF5LocalWriter;
-import org.broad.igv.h5.HDFWriter;
-import org.broad.igv.renderer.RendererFactory;
-import org.broad.igv.renderer.RendererFactory.RendererType;
-import org.broad.igv.tools.StatusMonitor;
-import org.broad.igv.track.TrackProperties;
-import org.broad.igv.track.WindowFunction;
-import org.broad.igv.ui.IGVModel;
-import org.broad.igv.util.ColorUtilities;
-
-import java.util.*;
-
-/**
- * @author jrobinso
- */
-public abstract class AbstractProcessor {
-
-    private static Logger log = Logger.getLogger(AbstractProcessor.class);
-    private static int version = 3;
-    Dataset dataset;
-    private Genome genome;
-    private int tileSize = 700;
-    private int zoomMax = 10;
-    private int zoomMin = 2;
-    Set<String> chromosomeGroupCache = new HashSet<String>();
-    Set<String> zoomGroupCache = new HashSet<String>();
-    Set<String> datasetCache = new HashSet<String>();
-    static ProcessingUtils procUtils = new ProcessingUtils();
-    GenomeSummaryData genomeSummaryData;
-    StatusMonitor statusMonitor;
-    HDFWriter writer;
-    boolean inferZeroes = false;
-    Set<WindowFunction> windowFunctions;
-
-    /**
-     * Constructs ...
-     *
-     * @param dataset
-     * @param statusMonitor
-     */
-    public AbstractProcessor(Dataset dataset, StatusMonitor statusMonitor) {
-        this(dataset);
-        this.statusMonitor = statusMonitor;
-
-        // Hardcoded  TODO -- pass these in
-        windowFunctions = new HashSet();
-        windowFunctions.add(WindowFunction.median);
-        windowFunctions.add(WindowFunction.mean);
-        windowFunctions.add(WindowFunction.median);
-        windowFunctions.add(WindowFunction.percentile90);
-        windowFunctions.add(WindowFunction.percentile10);
-        windowFunctions.add(WindowFunction.max);
-    }
-
-    /**
-     * Constructs ...
-     *
-     * @param dataset
-     */
-    public AbstractProcessor(Dataset dataset) {
-        this.dataset = dataset;
-        String genomeId = dataset.getGenome();
-        if (genomeId == null) {
-            IGVModel.getInstance().getViewContext().getGenomeId();
-        }
-        genomeId = dataset.getGenome();
-        genome = GenomeManager.getInstance().getGenome(genomeId);
-        if (genome == null) {
-            throw new RuntimeException("Unknown genome: " + genomeId);
-        }
-        genomeSummaryData = new GenomeSummaryData(genome, dataset.getDataHeadings());
-    }
-
-    /**
-     * Method description
-     *
-     * @param outputFile
-     * @return
-     */
-    public boolean process(String outputFile) {
-        writer = new HDF5LocalWriter(); // new H5BinWriter(new File(outputFile)); //new ZipWriter(); //
-        int file = writer.createFile(outputFile);
-        try {
-
-            // openFile root group
-            int root = writer.openGroup(file, "/");
-            writeRootAttributes(root);
-
-            // Process features, and as a side effect compute bin information
-            double estTimeFraction = 0.1;    // Estimate of total time spent processing features
-            int featureGroup = writer.createGroup(root, "features");
-            Map<String, List<BinnedData>> binInfoMap = processFeatures(featureGroup,
-                    estTimeFraction);
-            writer.closeGroup(featureGroup);
-
-            int dataGroup = writer.createGroup(root, "data");
-
-            // Process data
-            // If the "name" attribute is set in a track line, and there is a single track in the
-            // dataset,  use this as the name of the track.  This special case is to accomodate
-            // wig files.
-            TrackProperties tp = dataset.getTrackProperties();
-            if ((tp.getName() != null) && (dataset.getDataHeadings().length == 1)) {
-                writer.createAndWriteStringDataset(dataGroup, "track.id",
-                        new String[]{tp.getName()});
-            } else {
-                writer.createAndWriteStringDataset(dataGroup, "track.id",
-                        dataset.getDataHeadings());
-            }
-
-            estTimeFraction = 0.7;
-            processData(dataGroup, binInfoMap, estTimeFraction);
-            writer.closeGroup(dataGroup);
-
-            // cleanup
-            writer.closeGroup(root);
-
-
-            return true;
-
-        } catch (InterruptedException ex) {
-            System.out.println("Thread interrupted" + ex.getMessage());
-
-            // TODO -- cleanup
-            return false;
-
-        } finally {
-            try {
-                writer.closeFile(file);
-            } catch (Exception e) {
-                if (!Thread.interrupted()) {
-
-                    // log.error(e);
-                }
-            }
-        }
-    }
-
-    /**
-     * Check the tread for interrupt status.  Used to support cancelling
-     */
-    private void checkForInterrupt() throws InterruptedException {
-        if (Thread.interrupted()) {
-            System.out.println("Interrupted");
-            throw new InterruptedException();
-        }
-    }
-
-    /**
-     * @param featureGroup
-     * @return
-     */
-    private Map<String, List<BinnedData>> processFeatures(int featureGroup, double estTimeFraction)
-            throws InterruptedException {
-
-        double progIncrement = (estTimeFraction * 100) / dataset.getChromosomes().length;
-
-        Map<String, List<BinnedData>> binInfoMap = new HashMap();
-        for (String chr : dataset.getChromosomes()) {
-
-            checkForInterrupt();
-
-            List<BinnedData> binnedData = processFeaturesForChromosome(chr, featureGroup);
-            if (binnedData != null) {
-                binInfoMap.put(chr, binnedData);
-            }
-
-            if (statusMonitor != null) {
-                statusMonitor.incrementPercentComplete(progIncrement);
-            }
-
-
-        }
-        binInfoMap.put(CHR_ALL, processFeaturesForChromosome(CHR_ALL, featureGroup));
-
-        return binInfoMap;
-    }
-
-    /**
-     * @param chr
-     * @param featureGroup
-     * @return
-     */
-    private List<BinnedData> processFeaturesForChromosome(String chr, int featureGroup)
-            throws InterruptedException {
-
-        // Chromosome c = genome.getChromosome(chr);
-        int maxLength = 0;
-
-        if (chr.equals(CHR_ALL)) {
-            int chrLength = (int) (genome.getLength() / 1000);
-            maxLength = chrLength;
-        } else {
-            Chromosome c = genome.getChromosome(chr);
-
-
-            if (c == null) {
-                System.out.println("Missing chromosome: " + chr);
-                return null;
-            }
-            System.out.println("Processing chromosome: " + chr);
-            int chrLength = c.getLength();
-            int[] locs = dataset.getEndLocations(chr);
-            if (locs == null) {
-                locs = dataset.getStartLocations(chr);
-            }
-
-            maxLength = Math.max(chrLength, locs[locs.length - 1]);
-
-            if (maxLength > chrLength + 1) {
-                log.info("Warning: " + chr + " max end location (" + maxLength + " exceeds chr length (" + chrLength + ") Wrong genome?");
-            }
-        }
-
-
-        int chrGroup = writer.createGroup(featureGroup, chr);
-
-        writer.writeAttribute(chrGroup, "length", maxLength);
-
-        List<BinnedData> binnedDataList = computeAllBins(chr, maxLength);
-
-        // Record maximum zoom level
-        int numZoomLevels = (binnedDataList.size() == 0)
-                ? 0 : binnedDataList.get(binnedDataList.size() - 1).getZoomLevel() + 1;
-
-        writer.writeAttribute(chrGroup, "zoom.levels", numZoomLevels);
-
-        for (BinnedData binnedData : binnedDataList) {
-
-            checkForInterrupt();
-
-
-            String zoomName = "z" + binnedData.getZoomLevel();
-            int zoomGroup = writer.createGroup(chrGroup, zoomName);
-
-            writer.writeAttribute(zoomGroup, "bin.size", binnedData.getBinSize());
-
-            double tileWidth = tileSize * binnedData.getBinSize();
-
-            writer.writeAttribute(zoomGroup, "tile.width", tileWidth);
-
-            // TODO  mean.count, data.count,  max.count
-            writer.writeAttribute(zoomGroup, "mean.count", binnedData.getMeanCount());
-            writer.writeAttribute(zoomGroup, "median.count", binnedData.getMedianCount());
-            writer.writeAttribute(zoomGroup, "max.count", binnedData.getMaxCount());
-            writer.writeAttribute(zoomGroup, "percentile90.count",
-                    binnedData.getPercentile90Count());
-
-            // Record bin starting startLocations
-            int[] locations = binnedData.getLocations();
-
-            writer.createAndWriteVectorDataset(zoomGroup, "start", locations);
-
-            // Record boundary indices (bin number) for each tile
-            int[] tileBoundaries = binnedData.getTileBoundaries();
-
-            writer.createAndWriteVectorDataset(zoomGroup, "tile.boundary", tileBoundaries);
-
-            // Record # pts for each bin
-            float[] ptsPerBin = binnedData.getCounts();
-
-            writer.createAndWriteVectorDataset(zoomGroup, "count", ptsPerBin);
-            writer.closeGroup(zoomGroup);
-        }
-
-        // Record unprocessed coordinates
-
-        int rawGroup = writer.createGroup(chrGroup, "raw");
-
-        int[] startLocations = getStartLocationsForChromosome(chr);
-        writer.createAndWriteVectorDataset(rawGroup, "start", startLocations);
-
-        int[] endLocations = getEndLocationsForChromosome(chr);
-        if (endLocations != null) {
-            writer.createAndWriteVectorDataset(rawGroup, "end", endLocations);
-            int longestFeature = 0;
-            for (int i = 1; i < startLocations.length; i++) {
-                longestFeature = Math.max(endLocations[i] - startLocations[i], longestFeature);
-            }
-            writer.writeAttribute(rawGroup, "longest.feature", new Integer(longestFeature));
-        }
-
-
-        String[] featureNames = dataset.getFeatureNames(chr);
-
-        if (featureNames != null) {
-            writer.createAndWriteStringDataset(rawGroup, "feature", featureNames);
-        }
-
-        assert (maxLength < Integer.MAX_VALUE);
-        recordRawIndex(rawGroup, (int) maxLength, startLocations);
-        writer.closeGroup(rawGroup);
-
-        //if (!chr.equals(IGVConstants.CHR_ALL) && (startLocations.length > 0)) {
-        //    genomeSummaryData.addLocations(chr, startLocations);
-        //}
-
-
-        writer.closeGroup(chrGroup);
-
-        return binnedDataList;
-    }
-
-    protected int getZoomMax() {
-        return zoomMax;
-    }
-
-    /**
-     * @param chr
-     * @param maxLength
-     * @return
-     */
-    private List<BinnedData> computeAllBins(String chr, long maxLength) {
-        double binCountCutoff = 3;
-        List<BinnedData> binInfoList = new ArrayList();
-        int adjustedZoomMax = (chr.equals(CHR_ALL) ? 1 : getZoomMax());
-
-        int[] startLocations = getStartLocationsForChromosome(chr);
-        int[] endLocations = getEndLocationsForChromosome(chr);
-
-        for (int z = 0; z < adjustedZoomMax; z++) {
-            int nTiles = (int) Math.pow(2, z);
-            int nBins = nTiles * this.tileSize;
-            double binSize = ((double) maxLength) / nBins;
-
-            if (binSize < 0) {
-                System.out.println("Negative bin size");
-            }
-
-
-            List<Bin> bins = allocateBins(chr, nBins, binSize, startLocations, endLocations);
-            BinnedData binInfo = computeBinnedData(z, maxLength, nTiles, bins, binSize);
-
-            binInfoList.add(binInfo);
-
-            if ((binInfo.getMeanCount() < binCountCutoff) && (z > zoomMin)) {
-                break;
-            }
-        }
-
-        return binInfoList;
-    }
-
-    protected abstract List<Bin> allocateBins(String chr, int nBins, double binSize,
-                                              int[] startLocations, int[] endLocations);
-
-    private void recordRawIndex(int groupId, int chrLength, int[] locations) {
-        double chunkSize = 10000;
-        int nChunks = (int) (chrLength / chunkSize) + 2;
-        int[] indices = new int[nChunks];
-        int i = 0;
-        int n = 0;
-
-        while ((n < nChunks) && (i < locations.length)) {
-            int boundary = (int) (n * chunkSize);
-
-            try {
-                while ((locations[i] < boundary) && (i < locations.length - 1)) {
-                    i++;
-                }
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-
-            indices[n] = i;
-            n++;
-        }
-
-        // If we haven't filled the index array it means we have run out of
-        // startLocations.  In other words there is no data (startLocations) in the
-        // remaining chunks.  Record there indeces = the max location index.
-        while (n < nChunks) {
-            indices[n] = locations.length - 1;
-            n++;
-        }
-
-        writer.writeAttribute(groupId, "index.span", chunkSize);
-        writer.createAndWriteVectorDataset(groupId, "index", indices);
-    }
-
-    private void recordStats(DataStatistics[] stats, int group) {
-        int nPts = stats.length;
-        float[] min = new float[nPts];
-        float[] mean = new float[nPts];
-        float[] max = new float[nPts];
-        float[] median = new float[nPts];
-        float[] percentile10 = new float[nPts];
-        float[] percentile90 = new float[nPts];
-        float[] percentile98 = new float[nPts];
-        float[] stddev = new float[nPts];
-        for (int i = 0; i < nPts; i++) {
-            DataStatistics stat = stats[i];
-            min[i] = (stat == null) ? Float.NaN : (float) stat.getMin();
-            mean[i] = (stat == null) ? Float.NaN : (float) stats[i].getMean();
-            max[i] = (stat == null) ? Float.NaN : (float) stats[i].getMax();
-            median[i] = (stat == null) ? Float.NaN : (float) stats[i].getMedian();
-            percentile10[i] = (stat == null) ? Float.NaN : (float) stats[i].getPercentile10();
-            percentile90[i] = (stat == null) ? Float.NaN : (float) stats[i].getPercentile90();
-            percentile98[i] = (stat == null) ? Float.NaN : (float) stats[i].getPercentile90();
-            min[i] = (stat == null) ? Float.NaN : (float) stats[i].getPercentile98();
-            stddev[i] = (stat == null) ? Float.NaN : (float) stats[i].getStdDev();
-        }
-
-        writer.createAndWriteVectorDataset(group, "min", min);
-        writer.createAndWriteVectorDataset(group, "mean", mean);
-        writer.createAndWriteVectorDataset(group, "max", max);
-        writer.createAndWriteVectorDataset(group, "median", median);
-        writer.createAndWriteVectorDataset(group, "percentile10", percentile10);
-        writer.createAndWriteVectorDataset(group, "percentile90", percentile90);
-        writer.createAndWriteVectorDataset(group, "percentile98", percentile98);
-        writer.createAndWriteVectorDataset(group, "stddev", stddev);
-
-    }
-
-    // TODO consider making dataGroup an instance variable.  There is one
-    // per file, it doesn't change
-    /**
-     * Method description
-     *
-     * @param chr
-     * @param dataGroup
-     * @return
-     */
-    public int openChromosomeGroup(String chr, int dataGroup) {
-        if (chromosomeGroupCache.contains(chr)) {
-            return writer.openGroup(dataGroup, chr);
-        } else {
-            int chrGroup = writer.createGroup(dataGroup, chr);
-
-            chromosomeGroupCache.add(chr);
-
-            return chrGroup;
-        }
-    }
-
-    /**
-     * Method description
-     *
-     * @param chr
-     * @param zoomName
-     * @param chrGroup
-     * @return
-     */
-    public int openZoomGroup(String chr, String zoomName, int chrGroup) {
-        String key = chr + zoomName;
-
-        if (zoomGroupCache.contains(key)) {
-            return writer.openGroup(chrGroup, zoomName);
-        } else {
-            int zoomGroup = writer.createGroup(chrGroup, zoomName);
-
-            zoomGroupCache.add(key);
-
-            return zoomGroup;
-        }
-    }
-
-    /**
-     * Open a 2-D dataArrayDataset, create if neccessary.
-     *
-     * @param chr
-     * @param zoomName
-     * @param dsName
-     * @param nCols
-     * @param zoomGroup
-     * @return
-     */
-    public int openDataset(String chr, String zoomName, String dsName, int nCols, int zoomGroup) {
-
-        String key = chr + zoomName + dsName;
-
-
-        if (datasetCache.contains(key)) {
-            int datasetId = writer.openDataset(zoomGroup, dsName);
-
-            return datasetId;
-        } else {
-            int nRows = dataset.getDataHeadings().length;
-            int datasetId = writer.createDataset(zoomGroup, dsName, HDF5Constants.H5T_NATIVE_FLOAT,
-                    new long[]{nRows,
-                            nCols});
-
-            datasetCache.add(key);
-
-            return datasetId;
-        }
-    }
-
-    /**
-     * @param chr
-     * @param zoomName
-     * @param dsName
-     * @param zoomGroup
-     * @return
-     */
-    public int openVectorDataset(String chr, String zoomName, String dsName, int zoomGroup) {
-        String key = chr + zoomName + dsName;
-
-        if (datasetCache.contains(key)) {
-            int datasetId = writer.openDataset(zoomGroup, dsName);
-
-            return datasetId;
-        } else {
-            int nRows = dataset.getDataHeadings().length;
-            int datasetId = writer.createDataset(zoomGroup, dsName, HDF5Constants.H5T_NATIVE_FLOAT,
-                    new long[]{nRows});
-
-            datasetCache.add(key);
-
-            return datasetId;
-        }
-    }
-
-    private List<String> getAllChromosomes() {
-        List<String> allChromosomes = new ArrayList(Arrays.asList(dataset.getChromosomes()));
-        allChromosomes.add(CHR_ALL);
-        return allChromosomes;
-
-    }
-
-    private void processData(int dataGroup, Map<String, List<BinnedData>> binInfoMap,
-                             double estTimeFraction)
-            throws InterruptedException {
-
-        List<String> allChromosomes = getAllChromosomes();
-
-        int nSteps = allChromosomes.size() * dataset.getDataHeadings().length;
-        double procProgIncrement = (estTimeFraction * 0.8 * 100) / nSteps;
-        double rawDataProgIncrement = (estTimeFraction * 0.2 * 100) / nSteps;
-
-        // Loop through chromosomes
-        for (String chr : allChromosomes) {
-
-            checkForInterrupt();
-
-            if (chr.equals(CHR_ALL) || (genome.getChromosome(chr) != null)) {
-
-                int chrGroup = openChromosomeGroup(chr, dataGroup);
-
-                // Loop through samples
-                int rowNumber = 0;
-                boolean hasNulls = false;
-                int nCols = 0;
-                float[][] allData = new float[dataset.getDataHeadings().length][];
-                DataStatistics[] stats = new DataStatistics[dataset.getDataHeadings().length];
-                for (String heading : dataset.getDataHeadings()) {
-
-                    float[] data = this.getDataForChromosome(heading, chr);
-                    allData[rowNumber] = data;
-
-                    if ((data == null) || (data.length == 0)) {
-                        allData[rowNumber] = null;
-                        stats[rowNumber] = null;
-                        log.info("No data for array: " + heading + " chr: " + chr);
-                    } else {
-                        checkForInterrupt();
-                        nCols = data.length;
-                        processDataForChromosome(rowNumber, heading, chrGroup, binInfoMap, chr);
-
-                        if (statusMonitor != null) {
-                            statusMonitor.incrementPercentComplete(procProgIncrement);
-                        }
-
-                        if (statusMonitor != null) {
-                            statusMonitor.incrementPercentComplete(rawDataProgIncrement);
-                        }
-
-                        //genomeSummaryData.addData(heading, chr, data);
-
-                        stats[rowNumber] = ProcessingUtils.computeStats(data);
-
-                    }
-
-                    rowNumber++;
-                }
-
-                // If there are any null rows replace them with NaN
-                if (hasNulls && (nCols > 0)) {
-                    float[] nanArray = new float[nCols];
-                    Arrays.fill(nanArray, Float.NaN);
-                    for (int i = 0; i < allData.length; i++) {
-                        if (allData[i] == null) {
-                            allData[i] = nanArray;
-                        }
-                    }
-                }
-
-                int rawGroup = openZoomGroup(chr, "raw", chrGroup);
-
-                writer.createAndWriteDataset(rawGroup, "value", allData);
-                recordStats(stats, rawGroup);
-
-                writer.closeGroup(rawGroup);
-
-                writer.closeGroup(chrGroup);
-            }
-        }
-    }
-
-    float[] getDataForChromosome(String sample, String chr) {
-        if (chr.equals(CHR_ALL)) {
-
-            // TODO
-            return genomeSummaryData.getData(sample);
-        } else {
-            return dataset.getData(sample, chr);
-        }
-    }
-
-    int[] getStartLocationsForChromosome(String chr) {
-        if (chr.equals(CHR_ALL)) {
-            return genomeSummaryData.getLocations();
-        } else {
-            return dataset.getStartLocations(chr);
-        }
-    }
-
-    int[] getEndLocationsForChromosome(String chr) {
-        if (chr.equals(CHR_ALL)) {
-            return null;
-        } else {
-            return dataset.getEndLocations(chr);
-        }
-    }
-
-    /**
-     * @param rowNumber
-     * @param sample
-     * @param chrGroup
-     * @param binInfoMap
-     * @param chr
-     */
-    private void processDataForChromosome(int rowNumber, String sample, int chrGroup,
-                                          Map<String, List<BinnedData>> binInfoMap, String chr)
-            throws InterruptedException {
-
-        // Loop through zoom levels
-        for (BinnedData binInfo : binInfoMap.get(chr)) {
-
-            checkForInterrupt();
-
-            List<? extends Bin> bins = binInfo.getBins();
-
-            if (bins.size() > 0) {
-
-                // Arrays for the statistics, 1 element per bin.
-                float[] median = new float[bins.size()];
-                float[] percent10 = new float[bins.size()];
-                float[] percent90 = new float[bins.size()];
-                float[] min = new float[bins.size()];
-                float[] max = new float[bins.size()];
-                float[] mean = new float[bins.size()];
-                float[] stdDev = new float[bins.size()];
-
-                float[] data = getDataForChromosome(sample, chr);
-
-                for (int b = 0; b < bins.size(); b++) {
-                    Bin bin = bins.get(b);
-                    float[] binData = getDataForBin(data, bin);
-
-                    if (binData == null) {
-                        median[b] = percent10[b] = percent10[b] = max[b] = mean[b] = stdDev[b] =
-                                Float.NaN;
-                    } else {
-
-                        //
-                        DataStatistics stats = ProcessingUtils.computeStats(binData);
-
-                        median[b] = (float) stats.getMedian();
-                        percent10[b] = (float) stats.getPercentile10();
-                        percent90[b] = (float) stats.getPercentile90();
-                        max[b] = (float) stats.getMax();
-                        min[b] = (float) stats.getMin();
-                        mean[b] = (float) stats.getMean();
-                        stdDev[b] = (float) stats.getStdDev();
-                    }
-                }
-
-                String zoomName = "z" + binInfo.getZoomLevel();
-                int zoomGroup = openZoomGroup(chr, zoomName, chrGroup);
-
-                if (windowFunctions.contains(WindowFunction.median)) {
-                    recordStats("median", rowNumber, median, zoomGroup, chr, zoomName);
-                }
-
-                if (windowFunctions.contains(WindowFunction.percentile10)) {
-                    recordStats("percentile10", rowNumber, percent10, zoomGroup, chr, zoomName);
-                }
-                if (windowFunctions.contains(WindowFunction.percentile90)) {
-                    recordStats("percentile90", rowNumber, percent90, zoomGroup, chr, zoomName);
-                }
-                if (windowFunctions.contains(WindowFunction.min)) {
-                    recordStats("min", rowNumber, min, zoomGroup, chr, zoomName);
-                }
-                if (windowFunctions.contains(WindowFunction.max)) {
-                    recordStats("max", rowNumber, max, zoomGroup, chr, zoomName);
-                }
-                if (windowFunctions.contains(WindowFunction.mean)) {
-                    recordStats("mean", rowNumber, mean, zoomGroup, chr, zoomName);
-                }
-                if (windowFunctions.contains(WindowFunction.stddev)) {
-                    recordStats("stdDev", rowNumber, stdDev, zoomGroup, chr, zoomName);
-                }
-                writer.closeGroup(zoomGroup);
-            }
-        }
-    }
-
-    protected void recordStats(String type, int rowNumber, float[] data, int zoomGroup, String chr,
-                               String zoomName) {
-        int dataArrayDataset = openDataset(chr, zoomName, type, data.length, zoomGroup);
-
-        writer.writeDataRow(dataArrayDataset, rowNumber, data, data.length);
-        writer.closeDataset(dataArrayDataset);
-
-        float median = (float) procUtils.computeMedian(data);
-        int medianDataset = openVectorDataset(chr, zoomName, "median." + type, zoomGroup);
-
-        writer.writeDataValue(medianDataset, rowNumber, median);
-        writer.closeDataset(medianDataset);
-    }
-
-    protected abstract float[] getDataForBin(float[] data, Bin bin);
-
-    /**
-     * @param zoomLevel
-     * @param maxLength
-     * @param nTiles
-     * @param bins
-     * @param binSize
-     * @return
-     */
-    private BinnedData computeBinnedData(int zoomLevel, double chrLength, int nTiles,
-                                         List<Bin> bins, double binSize) {
-
-        // Find tile breaks.  Could possibly do this n loop above.
-        int[] tileBoundaries = new int[nTiles];
-        int binNumber = 0;
-        double tileLength = chrLength / nTiles;
-
-        for (int tileNumber = 0; (tileNumber < nTiles - 1) && (binNumber < bins.size());
-             tileNumber++) {
-
-            // Find end binIndex for this tile.  Using a linear search, might
-            // need to use a faster scheme.
-            while (bins.get(binNumber).getStart() < (tileNumber + 1) * tileLength) {
-                binNumber++;
-
-                if (binNumber == bins.size()) {
-                    break;
-                }
-            }
-
-            tileBoundaries[tileNumber] = binNumber;
-        }
-
-        // Boundary for last tile number is end
-        tileBoundaries[nTiles - 1] = bins.size() - 1;
-
-        BinnedData binInfo = new BinnedData(zoomLevel, binSize, bins, tileBoundaries);
-
-        // Compute the mean, data, and 90th percentile of occupied beans.
-        float mean = 0.0F;
-        float max = 0.0F;
-        DoubleArrayList percentiles = new DoubleArrayList(3);
-
-        percentiles.add(0.1);
-        percentiles.add(0.5);
-        percentiles.add(0.96);
-
-        DoubleQuantileFinder qf = QuantileFinderFactory.newDoubleQuantileFinder(false,
-                Long.MAX_VALUE, 0.0010, 1.0E-4, percentiles.size(), null);
-
-        for (Bin bin : bins) {
-            int count = bin.getFeatureCount();
-
-            mean += count;
-            max = Math.max(max, count);
-            qf.add(count);
-        }
-
-        binInfo.setMeanCount(mean / bins.size());
-        binInfo.setMaxCount(max);
-
-        DoubleArrayList quantiles = qf.quantileElements(percentiles);
-
-        binInfo.setPercentile10(quantiles.get(0));
-        binInfo.setMedianCount(quantiles.get(1));
-        binInfo.setPercentile90(quantiles.get(2));
-
-        return binInfo;
-    }
-
-    /**
-     * Method description
-     *
-     * @param zoomMax
-     */
-    public void setZoomMax(int zoomMax) {
-        this.zoomMax = zoomMax;
-    }
-
-    private void writeRootAttributes(int root) {
-
-        writer.writeAttribute(root, "name", dataset.getName());
-        writer.writeAttribute(root, "has.data", 1);
-        writer.writeAttribute(root, "normalized", dataset.isLogNormalized() ? 1 : 0);
-        writer.writeAttribute(root, "log.values", dataset.isLogNormalized() ? 1 : 0);
-        writer.writeAttribute(root, "version", version);
-        writer.writeAttribute(root, "type", dataset.getType());
-
-        if (dataset.getWindowSpan() > 0) {
-            writer.writeAttribute(root, "window.span", dataset.getWindowSpan());
-        }
-
-        // Track properties.  These properties will apply to all tracks in this dataset
-        TrackProperties properties = dataset.getTrackProperties();
-
-        if (properties.getAltColor() != null) {
-            writer.writeAttribute(root, "track.altColor",
-                    ColorUtilities.convertColorToRGBString(properties.getAltColor()));
-        }
-        if (properties.getMidColor() != null) {
-            writer.writeAttribute(root, "track.altColor",
-                    ColorUtilities.convertColorToRGBString(properties.getMidColor()));
-        }
-        if (properties.getColor() != null) {
-            writer.writeAttribute(root, "track.color",
-                    ColorUtilities.convertColorToRGBString(properties.getColor()));
-
-        }
-        if (properties.getDescription() != null) {
-            writer.writeAttribute(root, "track.description", properties.getDescription());
-        }
-        if (properties.getGenome() != null) {
-        }
-        if (properties.getHeight() > 0) {
-            writer.writeAttribute(root, "track.height", properties.getHeight());
-
-        }
-        writer.writeAttribute(root, "track.autoscale", String.valueOf(properties.isAutoScale()));
-
-        // autoscale
-        if (!Float.isNaN(properties.getMinValue())) {
-            writer.writeAttribute(root, "track.minValue", (double) properties.getMinValue());
-        }
-        if (!Float.isNaN(properties.getMidValue())) {
-            writer.writeAttribute(root, "track.midValue", (double) properties.getMidValue());
-        }
-        if (!Float.isNaN(properties.getMaxValue())) {
-            writer.writeAttribute(root, "track.maxValue", (double) properties.getMaxValue());
-        }
-        writer.writeAttribute(root, "track.drawMidValue", String.valueOf(properties.isDrawMidValue()));
-
-        if (properties.getName() != null) {
-            writer.writeAttribute(root, "track.name", properties.getName());
-        }
-        if (properties.getOffset() > 0) {
-        }
-        if (properties.getRendererClass() != null) {
-            RendererType rType = RendererFactory.getRenderType(properties.getRendererClass());
-
-            if (rType != null) {
-                writer.writeAttribute(root, "track.renderer", rType.toString());
-            }
-        }
-
-        if (properties.getUrl() != null) {
-        }
-        if (properties.getWindowingFunction() != null) {
-            writer.writeAttribute(root, "track.windowFunction",
-                    properties.getWindowingFunction().toString());
-
-        }
-    }
-}
diff --git a/src/org/broad/igv/preprocess/old/AffyAnnotationParser.java b/src/org/broad/igv/preprocess/old/AffyAnnotationParser.java
deleted file mode 100644
index 9be0bf0..0000000
--- a/src/org/broad/igv/preprocess/old/AffyAnnotationParser.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * AffyAnnotationParser.java
- *
- * Created on October 31, 2007, 8:30 AM
- *
- * Parse an affy probe annotation csv file
- */
-package org.broad.igv.preprocess.old;
-
-import org.broad.igv.data.ExpressionProbe;
-import org.broad.igv.data.ProbeSet;
-import org.broad.igv.feature.Feature;
-import org.broad.igv.feature.FeatureDB;
-import org.broad.igv.feature.GeneManager;
-import org.broad.igv.feature.Genome;
-import org.broad.igv.util.ParsingUtils;
-import org.broad.igv.ui.IGVModel;
-
-import java.io.*;
-
-/**
- * @author jrobinso
- */
-public class AffyAnnotationParser {
-
-    /**
-     * Parse the probe annotation file and return a list of ExpressionProbes.  If
-     * geneData is not null the probes will include gene info and be sorted by
-     * chromosome.
-     */
-    public static ProbeSet parseFile(File probeFile, GeneManager geneData) {
-
-        if (geneData == null) {
-            System.out.println("null geneData");
-            return null;
-        }
-        ProbeSet probeList = new ProbeSet();
-
-
-        try {
-            Genome genome = IGVModel.getInstance().getViewContext().getGenome();
-
-            int nLine = 0;
-
-            PrintWriter pw = new PrintWriter(new FileWriter("Unmatched.tab"));
-
-            BufferedReader reader = new BufferedReader(new FileReader(probeFile));
-            String nextLine = reader.readLine();
-            while ((nextLine = reader.readLine()) != null) {
-                nLine++;
-                String[] tokens = nextLine.split("\",\"");
-                if (tokens.length > 14) {
-
-                    String probeId = tokens[0].replace('\"', ' ').trim().toLowerCase();
-                    String geneId = genome.getChromosomeAlias(tokens[14].replace('\"', ' ').trim());
-
-                    ExpressionProbe probe = new ExpressionProbe(probeId, geneId);
-                    Feature gene = FeatureDB.getFeature(geneId);
-
-                    // Not interested in probes without gene mappings
-                    if (gene != null) {
-                        probe.setChr(gene.getChromosome());
-                        probe.setStart((int) gene.getStart());
-                        probe.setEnd((int) gene.getEnd());
-                        probeList.add(probe);
-
-                    } else {
-                        pw.println(probe + "\t" + gene);
-                    }
-                }
-            }
-            System.out.println("#lines = " + nLine);
-            reader.close();
-            pw.close();
-
-
-        } catch (FileNotFoundException ex) {
-            ex.printStackTrace();
-        } catch (IOException ex) {
-            ex.printStackTrace();
-        }
-
-        return probeList;
-    }
-}
-
-
-
-
diff --git a/src/org/broad/igv/preprocess/old/AffyProbeListUtilities.java b/src/org/broad/igv/preprocess/old/AffyProbeListUtilities.java
deleted file mode 100644
index dc08d4b..0000000
--- a/src/org/broad/igv/preprocess/old/AffyProbeListUtilities.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-
-package org.broad.igv.preprocess.old;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import org.broad.igv.data.ExpressionProbe;
-import org.broad.igv.data.ProbeSet;
-import org.broad.igv.feature.Feature;
-import org.broad.igv.feature.GeneManager;
-import org.broad.igv.feature.UCSCGeneTableParser;
-import org.broad.igv.ui.MiscStuff;
-import org.broad.igv.util.AsciiLineReader;
-
-import java.io.*;
-import java.util.List;
-
-/**
- * Utlity to create a probe list from an affy annotation file
- *
- * @author jrobinso
- */
-public class AffyProbeListUtilities {
-
-    static String genome = "hg18";
-    static File annotFile =
-            new File("../../gp/SnpViewer/data/jamboree/HT_HG-U133A.na23.annot.csv");
-
-    /**
-     * Method description
-     *
-     * @param args
-     * @throws IOException
-     */
-    public static void main(String[] args) throws IOException {
-        String gctFile =
-                "/Users/jrobinso/IGVTestData/PrimaryGBMs.43.use_for_outlier_analysis.centered_and_normalized.gct";
-        addGenesToGctFile(gctFile);
-    }
-
-    /**
-     * Method description
-     *
-     * @param gctFile
-     * @throws IOException
-     */
-    public static void addGenesToGctFile(String gctFile) throws IOException {
-
-        File outputFile = new File("fixed.gct");
-        GeneManager geneData = new GeneManager(genome, "Gene");
-        String refGeneFile = genome + "_refFlat.txt";
-        InputStream is2 = MiscStuff.class.getResourceAsStream("/resources/"
-                + refGeneFile);
-        AsciiLineReader reader2 = new AsciiLineReader(is2);
-        List<Feature> genes = (new UCSCGeneTableParser(UCSCGeneTableParser.Type.REFFLAT)).loadFeatures(reader2);
-        for (Feature gene : genes) {
-            geneData.addGene(gene);
-        }
-        geneData.sortGeneLists();
-        reader2.close();
-
-        ProbeSet pset = AffyAnnotationParser.parseFile(annotFile, geneData);
-
-        reader2 = new AsciiLineReader(new FileInputStream(gctFile));
-        PrintWriter pw = new PrintWriter(new FileWriter(outputFile));
-
-        String nextLine = reader2.readLine();    // header, skip
-        reader2.readLine();
-        reader2.readLine();
-
-        int noMatch = 0;
-        while ((nextLine = reader2.readLine()) != null) {
-            String[] tokens = nextLine.split("\t");
-            if (tokens.length < 2) {
-                pw.println(nextLine);
-
-            } else {
-                String probeId = tokens[0].trim();
-                String desc = tokens[1].trim();
-                ExpressionProbe probe = pset.getProbe(probeId);
-                if (probe == null) {
-                    noMatch++;
-                    pw.println(nextLine);
-                } else {
-                    pw.print(probeId + "\t");
-                    pw.print(desc);
-                    pw.print(" |@" + probe.getFeature() + "|");
-
-                    for (int i = 2; i < tokens.length; i++) {
-                        pw.print("\t" + tokens[i]);
-                    }
-                    pw.println();
-                }
-            }
-        }
-
-        pw.close();
-        reader2.close();
-
-        System.out.println("# rows not matched = " + noMatch);
-    }
-
-    /**
-     * Method description
-     *
-     * @throws IOException
-     */
-    public static void createProbeMappingFile() throws IOException {
-
-        File outputFile = new File("probes.hg18.tab");
-        GeneManager geneData = new GeneManager(genome, "Gene");
-        String refGeneFile = genome + "_refFlat.txt";
-        InputStream is2 = MiscStuff.class.getResourceAsStream("/resources/"
-                + refGeneFile);
-        AsciiLineReader reader2 = new AsciiLineReader(is2);
-        List<Feature> genes = (new UCSCGeneTableParser(UCSCGeneTableParser.Type.REFFLAT)).loadFeatures(reader2);
-        for (Feature gene : genes) {
-            geneData.addGene(gene);
-        }
-        geneData.sortGeneLists();
-        reader2.close();
-
-        ProbeSet pset = AffyAnnotationParser.parseFile(annotFile, geneData);
-
-        PrintWriter pw = new PrintWriter(new FileWriter(outputFile));
-        for (String chr : pset.getChromosomes()) {
-            for (ExpressionProbe ep : pset.getProbes(chr)) {
-                pw.println(ep.getName() + "\t" + ep.getChr() + "\t"
-                        + ep.getStart() + "\t" + ep.getEnd());
-            }
-        }
-        pw.close();
-    }
-
-    /**
-     * Method description
-     *
-     * @throws IOException
-     */
-    public static void createGeneMappingFile() throws IOException {
-
-        File outputFile = new File("genes.hg18.tab");
-        GeneManager geneData = new GeneManager(genome, "Gene");
-        String refGeneFile = genome + "_refFlat.txt";
-        InputStream is2 = MiscStuff.class.getResourceAsStream("/resources/"
-                + refGeneFile);
-        AsciiLineReader reader2 = new AsciiLineReader(is2);
-        List<Feature> genes = (new UCSCGeneTableParser(UCSCGeneTableParser.Type.REFFLAT)).loadFeatures(reader2);
-        for (Feature gene : genes) {
-            geneData.addGene(gene);
-        }
-        geneData.sortGeneLists();
-        reader2.close();
-
-        PrintWriter pw = new PrintWriter(new FileWriter(outputFile));
-        for (String chr : geneData.getChromosomes()) {
-            for (Feature gene : geneData.getGenesForChromosome(chr)) {
-                pw.println(gene.getName() + "\t" + chr + "\t" + gene.getStart()
-                        + "\t" + gene.getEnd());
-            }
-        }
-
-        pw.close();
-    }
-}
diff --git a/src/org/broad/igv/preprocess/old/BEDFeatureProcessor.java b/src/org/broad/igv/preprocess/old/BEDFeatureProcessor.java
deleted file mode 100644
index 5af635c..0000000
--- a/src/org/broad/igv/preprocess/old/BEDFeatureProcessor.java
+++ /dev/null
@@ -1,387 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * AbstractProcessor.java
- *
- * Created on Sep 26, 2007, 4:41:31 PM
- *
- * To change this template, choose Tools | Templates
- * and openFile the template in the editor.
- */
-package org.broad.igv.preprocess.old;
-
-import cern.colt.list.DoubleArrayList;
-import cern.jet.stat.quantile.DoubleQuantileFinder;
-import cern.jet.stat.quantile.QuantileFinderFactory;
-import org.broad.igv.feature.Chromosome;
-import org.broad.igv.feature.Feature;
-import org.broad.igv.feature.FeatureUtils;
-import org.broad.igv.feature.Genome;
-import org.broad.igv.h5.HDF5LocalWriter;
-import org.broad.igv.h5.HDFWriter;
-
-import java.io.File;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-
-/**
- * @author jrobinso
- */
-public class BEDFeatureProcessor {
-
-    static int zoomMax = 3;
-    static int tileWidthPixels = 700;
-
-    static HDFWriter writer = new HDF5LocalWriter();
-
-    /**
-     * Example usage  BedFeatureProcessor
-     * -inputFile data/FeatureTracks/071109_mm8_TUset_CompleteGenome_v2.bed
-     * -ouputFile data/071109_mm8_TUset_CompleteGenome_v2.h5
-     * -genome mm8
-     *
-     * @param args
-     */
-    public static void main(String[] args) {
-
-
-        LinkedHashMap<String, String> argsMap = parseArgs(args);
-        String inputFile = argsMap.get("-inputFile");
-        String outputFile = argsMap.get("-outputFile");
-        String genomeName = argsMap.get("-genome");
-        String name = argsMap.get("-name");
-
-        if (inputFile == null || outputFile == null || genomeName == null) {
-            System.out.println("Usage BEDFeatureProcessor -inputFile <file> -outputFile <file> -genome <mm8> -name <optional>");
-            System.exit(-1);
-        }
-
-        File featureFile = new File(inputFile);
-        if (name == null) {
-            name = featureFile.getName();
-        }
-
-    }
-
-
-    /**
-     *
-     */
-    public static void processFeatures(String filename, Genome genome, List<Feature> allFeatures, String name) {
-
-        int file = writer.createFile(filename);
-
-        // openFile root group
-        int root = writer.openGroup(file, "/");
-
-        // Record type
-        writer.writeAttribute(root, "name", name);
-        writer.writeAttribute(root, "type", "FEATURE");
-        writer.writeAttribute(root, "has.data", 0);
-
-
-        // Create a group for this feature track.  Might store multiple tracks
-        // in one HDF5 file in the future.
-        int featureGroup = writer.createGroup(root, "features");
-
-
-        // Divide features by chromosome
-        Map<String, List<Feature>> featureMap = FeatureUtils.divideByChromosome(allFeatures);
-
-        // Loop through chromosomes
-        for (String chr : featureMap.keySet()) {
-
-            Chromosome chromosome = genome.getChromosome(chr);
-            if (chromosome == null) {
-                System.out.println("No chromosome: " + chr);
-            } else {
-                int chrLength = (int) chromosome.getLength();
-                List<Feature> features = featureMap.get(chr);
-                FeatureUtils.sortFeatureList(features);
-
-
-                // TODO find out where "chr"  is getting stripped off chr string
-                int featureChrGroup = writer.createGroup(featureGroup, chr);
-
-                writer.writeAttribute(featureChrGroup, "length", chrLength);
-
-                int maxZoom = processZoomLevels(featureChrGroup, chrLength, features);
-
-                writer.writeAttribute(featureChrGroup, "zoom.levels", maxZoom);
-
-                boolean hasStrand = features.get(0).hasStrand();
-                boolean hasScore = features.get(0).hasScore();
-                processRawData(featureChrGroup, chrLength, features, maxZoom,
-                        hasScore, hasStrand);
-
-
-                writer.closeGroup(featureChrGroup);
-            }
-        }
-
-        writer.closeGroup(featureGroup);
-
-
-        // cleanup
-        writer.closeGroup(root);
-        writer.closeFile(file);
-    }
-
-    private static void processRawData(int chrGroup, int chrLength,
-                                       List<Feature> features, int maxZoom,
-                                       boolean hasScore, boolean hasStrand) {
-
-        int rawGroup = writer.createGroup(chrGroup, "raw");
-
-        int nFeatures = features.size();
-        int[] start = new int[nFeatures];
-        int[] end = new int[nFeatures];
-        String[] name = new String[nFeatures];
-        float[] score = hasScore ? new float[nFeatures] : null;
-        char[] strand = hasStrand ? new char[nFeatures] : null;
-
-        int maxStringWidth = 0;
-        for (int i = 0; i < nFeatures; i++) {
-            Feature f = features.get(i);
-            start[i] = (int) f.getStart();
-            end[i] = (int) f.getEnd();
-            name[i] = f.getName();
-            maxStringWidth = Math.max(maxStringWidth, name[i].length());
-            if (score != null) {
-                score[i] = f.getScore();
-            }
-            //if(strand != null) strand[i] = f.getStrand();
-        }
-
-        writer.createAndWriteVectorDataset(rawGroup, "start", start);
-        writer.createAndWriteVectorDataset(rawGroup, "end", end);
-        writer.createAndWriteStringDataset(rawGroup, "name", name, maxStringWidth);
-        if (score != null) {
-            writer.createAndWriteVectorDataset(rawGroup, "score", score);
-        }
-        //if(strand != null) writer.createAndWriteVectorDataset(rawGroup, "strand", strand);
-        recordRawIndex(rawGroup, chrLength, features);
-
-        writer.closeGroup(rawGroup);
-    }
-
-    /**
-     * TODO  THIS IS NEARLY AN  EXACT COPY FROM SnpSolexaProcessor.  REFACTOR TO
-     * COMMON CLASS.  THE ONLY DIFFERENCE IS THE REPLACEMENT OF locations  with
-     * features.
-     * Compute the tile boundaries for the raw data.
-     * zoomMax is the maximum zoom level recorded.  Use to size tiles.
-     */
-    private static void recordRawIndex(int groupId, int chrLength, List<Feature> features) {
-
-        // Chunk size in base pairs.  Assume features are evenly distributed, set
-        // set size to encompass approximately 1000 features per chunk, but no larger
-        // than 1/4 the chr length
-
-        double featuresPerBp = ((double) features.size()) / chrLength;
-        double chunkSize = Math.min(chrLength / 4, 1000 / featuresPerBp);
-        int nChunks = (int) (chrLength / chunkSize) + 1;
-
-        int[] indices = new int[nChunks];
-
-        int i = 0;
-        int n = 0;
-        while (n < nChunks && i < features.size()) {
-            int boundary = (int) (n * chunkSize);
-
-            // Skip until we have crossed the boundary
-            try {
-                while (features.get(i).getStart() < boundary && i < features.size() - 1) {
-                    i++;
-                }
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-
-            indices[n] = i;
-
-            n++;
-
-        }
-
-        // If we haven't filled the index array it means we have run out of 
-        // locations.  In other words there is no data (locations) in the 
-        // remaining chunks.  Record there indeces = the max location index.
-        while (n < nChunks) {
-            indices[n] = features.size() - 1;
-            n++;
-        }
-
-        writer.writeAttribute(groupId, "index.span", chunkSize);
-        writer.createAndWriteVectorDataset(groupId, "index", indices);
-
-    }
-
-    /**
-     * Note:  method body nearly exacty copy of expression processor
-     * <p/>
-     * Return the maximum zoom level computed
-     */
-    private static int processZoomLevels(int featureChrGroup, int chrLength, List<Feature> features) {
-
-        int z = 0;
-
-        //TODO -- get smarter about computing max zoom level.  Maybe look at feature counts.
-        while (z < zoomMax) {
-
-            BinnedData binInfo = computeBinnedData(chrLength, z, features);
-            if (binInfo.getLocations().length == 0) {
-                System.out.println("Zero lengh locations: " + z);
-                return z;
-            }
-
-            String zoomName = "z" + binInfo.getZoomLevel();
-            int featureZoomGroup = writer.createGroup(featureChrGroup, zoomName);
-
-            // Compute counts in features / KBs
-            float[] counts = binInfo.getCounts();
-
-            // TODO nTiles is also calculated in computeBinnedData.  do it in one place
-            int nTiles = (int) Math.pow(2, z);
-            double tileWidth = chrLength / nTiles;
-
-            writer.writeAttribute(featureZoomGroup, "tile.width", tileWidth);
-            writer.writeAttribute(featureZoomGroup, "bin.size", binInfo.getBinSize());
-            writer.writeAttribute(featureZoomGroup, "mean.count", binInfo.getMeanCount());
-            writer.writeAttribute(featureZoomGroup, "median.count", binInfo.getMedianCount());
-            writer.writeAttribute(featureZoomGroup, "max.count", binInfo.getMaxCount());
-
-            // Record bin start locations
-            int[] locations = binInfo.getLocations();
-            writer.createAndWriteVectorDataset(featureZoomGroup, "start", locations);
-
-            // Record the number of data points for each bin
-
-            // Record boundary indices (bin number) for each tile
-            int[] tileBoundaries = binInfo.getTileBoundaries();
-            writer.createAndWriteVectorDataset(featureZoomGroup, "tile.boundary", tileBoundaries);
-
-            writer.createAndWriteVectorDataset(featureZoomGroup, "count", counts);
-
-            writer.closeGroup(featureZoomGroup);
-
-            z++;
-
-            if (binInfo.getMeanCount() < 3) {
-                return z;
-            }
-
-        }
-
-        return z;
-    }
-
-    /**
-     * Allocate features to bins for a zoom level.  A single probe can be spread across multiple
-     * bin, and a single bin can have contributions from multiple genes.
-     */
-    public static BinnedData computeBinnedData(int chrLength, int zoomLevel, List<Feature> features) {
-
-
-        // Now remove empty bins & count the number of bins with multiple features
-        //int nPackedBins = 0;
-        int nTiles = (int) Math.pow(2, zoomLevel);
-        int nBins = nTiles * tileWidthPixels;
-        double binSize = ((double) chrLength) / nBins;
-
-
-        List<FeatureBin> occupiedBins = (new FeatureBinCalculator()).computeFeatureBins(features, nBins, binSize, 0, chrLength);
-
-        // Find tile breaks.  Could possibly do this n loop above.
-        int[] tileBoundaries = new int[nTiles];
-        int binNumber = 0;
-        double tileLength = chrLength / nTiles;
-        for (int tileNumber = 0; tileNumber < nTiles - 1; tileNumber++) {
-            // Find end bin for this tile.  Using a linear search, might
-            // need to use a faster scheme.
-            if (!occupiedBins.isEmpty()) {
-                while (binNumber < occupiedBins.size() &&
-                        occupiedBins.get(binNumber).getStart() < (tileNumber + 1) * tileLength) {
-                    binNumber++;
-                }
-
-            }
-            tileBoundaries[tileNumber] = binNumber;
-        }
-        // Boundary for last tile number is end
-        tileBoundaries[nTiles - 1] = occupiedBins.size() - 1;
-
-        BinnedData binInfo = new BinnedData(zoomLevel, binSize, occupiedBins, tileBoundaries);
-
-        // Compute the mean, median, and 90th percentile of occupied beans.
-        float mean = 0f;
-        float max = 0f;
-        DoubleArrayList percentiles = new DoubleArrayList(3);
-        percentiles.add(0.1);
-        percentiles.add(0.5);
-        percentiles.add(0.90);
-        DoubleQuantileFinder qf = QuantileFinderFactory.newDoubleQuantileFinder(false, Long.MAX_VALUE,
-                0.001, 0.0001, percentiles.size(), null);
-        for (Bin bin : occupiedBins) {
-            int count = bin.getFeatureCount();
-            mean += count;
-            max = Math.max(max, count);
-            qf.add(count);
-        }
-        DoubleArrayList quantiles = qf.quantileElements(percentiles);
-        binInfo.setMeanCount(mean / occupiedBins.size());
-        binInfo.setMaxCount(max);
-        binInfo.setPercentile10(quantiles.get(0));
-        binInfo.setMedianCount(quantiles.get(1));
-        binInfo.setPercentile90(quantiles.get(2));
-
-
-        return binInfo;
-
-    }
-
-    /**
-     * TODO move to utilitiy class
-     *
-     * @param args
-     * @return
-     */
-    private static LinkedHashMap<String, String> parseArgs(String[] args) {
-
-        LinkedHashMap<String, String> argMap = new LinkedHashMap();
-        for (int i = 0; i < args.length; i++) {
-            System.out.println(args[i]);
-            String key = args[i];
-            if (args[i].startsWith("-")) {
-                if (i < args.length - 1) {
-                    i++;
-                    argMap.put(key, args[i]);
-                } else {
-                    argMap.put(key, "");
-                }
-            } else {
-                argMap.put(key, key);
-            }
-        }
-        return argMap;
-
-    }
-}
diff --git a/src/org/broad/igv/preprocess/old/BatchSegProcessor.java b/src/org/broad/igv/preprocess/old/BatchSegProcessor.java
deleted file mode 100644
index f25dab0..0000000
--- a/src/org/broad/igv/preprocess/old/BatchSegProcessor.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-package org.broad.igv.preprocess.old;
-
-import java.io.File;
-import java.io.FileFilter;
-
-
-/**
- * Quick "hack" for gcm data
- */
-public class BatchSegProcessor {
-
-    public static void main(String[] args) {
-
-        File inputDirectory = new File("");
-        File outputDirector = new File("");
-
-        File xmlFile = new File("");
-
-
-        File[] segFiles = inputDirectory.listFiles(new FileFilter() {
-
-            public boolean accept(File arg0) {
-                return arg0.getName().endsWith(".seg");
-            }
-        });
-
-        String genomeId = "hg17";
-        for (File segFile : segFiles) {
-
-
-            // SegmentedDataWriter.main(args);
-        }
-
-
-    }
-}
-
diff --git a/src/org/broad/igv/preprocess/old/Bin.java b/src/org/broad/igv/preprocess/old/Bin.java
deleted file mode 100644
index d4f5082..0000000
--- a/src/org/broad/igv/preprocess/old/Bin.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-package org.broad.igv.preprocess.old;
-
-/**
- * @author jrobinso
- */
-public interface Bin {
-
-    public int getStart();
-
-    public int getFeatureCount();
-
-}
diff --git a/src/org/broad/igv/preprocess/old/BinIndexList.java b/src/org/broad/igv/preprocess/old/BinIndexList.java
deleted file mode 100644
index ebc8401..0000000
--- a/src/org/broad/igv/preprocess/old/BinIndexList.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-package org.broad.igv.preprocess.old;
-
-import java.util.Iterator;
-
-/**
- * @author jrobinso
- */
-public class BinIndexList implements Iterable {
-    private int startIndex;
-    private int endIndex;
-
-    public int getStartIndex() {
-        return startIndex;
-    }
-
-    public void setStartIndex(int startIndex) {
-        this.startIndex = startIndex;
-    }
-
-    public int getEndIndex() {
-        return endIndex;
-    }
-
-    public void setEndIndex(int endIndex) {
-        this.endIndex = endIndex;
-    }
-
-    public Iterator<Integer> iterator() {
-        return new BinIndexIterator<Integer>();
-    }
-
-    class BinIndexIterator<T> implements Iterator {
-
-        int nextIndex;
-
-        public BinIndexIterator() {
-            nextIndex = startIndex;
-
-        }
-
-        public boolean hasNext() {
-            return nextIndex < endIndex;
-        }
-
-        public T next() {
-            Integer retValue = new Integer(nextIndex);
-            nextIndex++;
-            return (T) retValue;
-        }
-
-        public void remove() {
-            throw new UnsupportedOperationException("Not supported yet.");
-        }
-
-
-    }
-
-    public static void main(String[] args) {
-
-        BinIndexList bil = new BinIndexList();
-        bil.setStartIndex(1);
-        bil.setEndIndex(10);
-        for (Object i : bil) {
-            System.out.println(i);
-        }
-    }
-
-
-}
diff --git a/src/org/broad/igv/preprocess/old/BinaryReader.java b/src/org/broad/igv/preprocess/old/BinaryReader.java
deleted file mode 100644
index a0243f5..0000000
--- a/src/org/broad/igv/preprocess/old/BinaryReader.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * BinaryParser.java
- *
- * Created on July 26, 2007, 1:01 AM
- *
- * To change this template, choose Tools | Template Manager
- * and open the template in the editor.
- */
-package org.broad.igv.preprocess.old;
-
-import org.j3d.io.EndianConverter;
-
-import java.io.*;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @author jrobinso
- */
-public class BinaryReader {
-
-    private boolean littleEndian = false;
-
-    /**
-     * Creates a new instance of BinaryParser
-     */
-    public BinaryReader() {
-    }
-
-    public BinaryReader(boolean littleEndian) {
-        this.setLittleEndian(littleEndian);
-    }
-
-    /**
-     * Read an array of floats from a  binary file.   It is assumed that the
-     * file contains floats written with DataOutput.writeFloat();
-     */
-    public float[] readFloats(File file) {
-        return convertBytesToFloats(readBytes(file));
-    }
-
-    /**
-     * Read an array of ints from the binary file.   It is assumed that the
-     * file contains ints written with DataOutput.writeInt();
-     */
-    /**
-     * Read an array of ints from the binary file.   It is assumed that the
-     * file contains ints written with DataOutput.writeInt();
-     */
-    public int[] readInts(File file) {
-        try {
-            int nInts = (int) (file.length() / (Integer.SIZE / 8));
-            int[] ints = new int[nInts];
-            DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
-            for (int i = 0; i < nInts; i++) {
-                if (dis.available() >= (Integer.SIZE / 8)) {
-                    ints[i] = dis.readInt();
-                }
-            }
-            dis.close();
-            return ints;
-        } catch (IOException ex) {
-            ex.printStackTrace();
-            throw new RuntimeException(ex);
-        }
-    }
-
-    /**
-     * Read an array of longs from the binary file.   It is assumed that the
-     * file contains longs written with DataOutput.writeInt();
-     */
-    public long[] readLongs(File file) {
-        try {
-            int nLongs = (int) (file.length() / (Long.SIZE / 8));
-            long[] longs = new long[nLongs];
-            DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
-            for (int i = 0; i < nLongs; i++) {
-                if (dis.available() >= (Long.SIZE / 8)) {
-                    longs[i] = dis.readLong();
-                }
-            }
-            dis.close();
-            return longs;
-        } catch (IOException ex) {
-            ex.printStackTrace();
-            throw new RuntimeException(ex);
-        }
-    }
-
-    protected byte[] readBytes(File file) {
-        try {
-            int fileSize = (int) file.length();
-            BufferedInputStream inStream = new BufferedInputStream(new FileInputStream(file), fileSize);
-            // Read in chunks of 100MB
-            byte[] bytes = readBytes(inStream, fileSize);
-            inStream.close();
-            return bytes;
-        } catch (IOException ex) {
-            ex.printStackTrace();
-            throw new RuntimeException(ex);
-        }
-
-    }
-
-    public List<File> getDataFiles(String dataFileDirectory) {
-        FilenameFilter filter = new FilenameFilter() {
-            public boolean accept(File dir, String name) {
-                return name.endsWith("bin");
-            }
-        };
-        return getDataFiles(dataFileDirectory, filter);
-    }
-
-
-    /**
-     * Return all data files in the data directory. This includes location,
-     * marker (snp) id, and copy number files for all chromosomes.
-     */
-    public List<File> getDataFiles(String dataFileDirectory, FilenameFilter filter) {
-        File dfd = new File(dataFileDirectory);
-        if (!dfd.exists()) {
-            System.err.println("Input directory: " + dfd.getAbsolutePath() + " does not exist.  Exiting!");
-            System.exit(-1);
-        }
-
-        File[] files = new File(dataFileDirectory).listFiles(filter);
-        return (files == null ? new ArrayList<File>() : java.util.Arrays.asList(files));
-    }
-
-
-    /**
-     * Read the number of bytes specificed from the input stream
-     */
-    protected byte[] readBytes(InputStream inStream, int nBytes) throws IOException {
-        byte[] bytes = new byte[nBytes];
-        int bytesRead = 0;
-        while (bytesRead < nBytes) {
-            bytesRead += inStream.read(bytes, bytesRead, inStream.available());
-        }
-        return bytes;
-    }
-
-    /**
-     * Convert bytes to floats.  Handles endian conversion
-     */
-    private float[] convertBytesToFloats(byte[] bytes) {
-        if (littleEndian) {
-            float[] floats = new float[bytes.length / (Float.SIZE / 8)];
-            EndianConverter.convertLittleEndianToFloat(bytes, floats, bytes.length, 0, floats.length);
-            return floats;
-        } else {
-            return ByteConverter.convertBytesToFloats(bytes);
-        }
-    }
-
-    public void setLittleEndian(boolean littleEndian) {
-        this.littleEndian = littleEndian;
-    }
-}
diff --git a/src/org/broad/igv/preprocess/old/BinnedData.java b/src/org/broad/igv/preprocess/old/BinnedData.java
deleted file mode 100644
index 46dcdb9..0000000
--- a/src/org/broad/igv/preprocess/old/BinnedData.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * BinnedData.java
- *
- * Created on October 29, 2007, 3:39 PM
- *
- * To change this template, choose Tools | Template Manager
- * and open the template in the editor.
- */
-
-package org.broad.igv.preprocess.old;
-
-import java.util.List;
-
-/**
- * @author jrobinso
- */
-public class BinnedData {
-
-    /**
-     * Zoom level represented by this collection of bins
-     */
-    int zoomLevel;
-
-    /**
-     * LocationBin size (or scale)
-     */
-    double binSize;
-
-    /**
-     * The mean number of data points in a bin
-     */
-    private double meanCount;
-
-    /**
-     * The median number of data points in a bin
-     */
-    private double medianCount;
-
-    /**
-     * The maximum # of data points in a bin
-     */
-    private double maxCount;
-
-    private double percentile10;
-
-    /**
-     * The 90th percentile
-     */
-    private double percentile90;
-
-    /**
-     * The number of data points per bin
-     */
-    private int[] nPts;
-
-    /**
-     * The bins
-     */
-    private List<? extends Bin> bins;
-
-    /**
-     * Tile boundaries.  Tiles are fixed length in genomic coordinates
-     */
-    private int[] tileBoundaries;
-
-    public BinnedData(int zoomLevel, double binSize, List<? extends Bin> bins, int[] tileBoundaries) {
-        this.zoomLevel = zoomLevel;
-        this.binSize = binSize;
-        this.bins = bins;
-        this.tileBoundaries = tileBoundaries;
-    }
-
-    public int getZoomLevel() {
-        return zoomLevel;
-    }
-
-    public List<? extends Bin> getBins() {
-        return bins;
-    }
-
-
-    public int[] getLocations() {
-        int[] locations = new int[bins.size()];
-        for (int i = 0; i < bins.size(); i++) {
-            locations[i] = bins.get(i).getStart();
-        }
-        return locations;
-    }
-
-    /**
-     * Used for feature processing
-     */
-    public float[] getCounts() {
-        float[] nPts = new float[bins.size()];
-        for (int i = 0; i < bins.size(); i++) {
-            nPts[i] = bins.get(i).getFeatureCount();
-        }
-        return nPts;
-    }
-
-    public int[] getTileBoundaries() {
-        return tileBoundaries;
-    }
-
-    public double getBinSize() {
-        return binSize;
-    }
-
-    public double getMeanCount() {
-        return meanCount;
-    }
-
-    public void setMeanCount(double meanCount) {
-        this.meanCount = meanCount;
-    }
-
-    public double getMedianCount() {
-        return medianCount;
-    }
-
-    public void setMedianCount(double medianCount) {
-        this.medianCount = medianCount;
-    }
-
-
-    public double getMaxCount() {
-        return maxCount;
-    }
-
-    public void setMaxCount(double maxCount) {
-        this.maxCount = maxCount;
-    }
-
-    public double getPercentile90Count() {
-        return percentile90;
-    }
-
-    public void setPercentile90(double percentile90) {
-        this.percentile90 = percentile90;
-    }
-
-    public double getPercentile10() {
-        return percentile10;
-    }
-
-    public void setPercentile10(double percentile10) {
-        this.percentile10 = percentile10;
-    }
-}
diff --git a/src/org/broad/igv/preprocess/old/ByteConverter.java b/src/org/broad/igv/preprocess/old/ByteConverter.java
deleted file mode 100644
index 1f74388..0000000
--- a/src/org/broad/igv/preprocess/old/ByteConverter.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * ByteConverter.java
- *
- * Created on August 15, 2007, 8:48 AM
- *
- * To change this template, choose Tools | Template Manager
- * and open the template in the editor.
- */
-
-package org.broad.igv.preprocess.old;
-
-import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
-import java.io.IOException;
-
-/**
- * @author jrobinso
- */
-public class ByteConverter {
-
-    public static float[] convertBytesToFloats(byte[] bytes) {
-        try {
-            int nFloats = bytes.length / (Float.SIZE / 8);
-            float[] floats = new float[nFloats];
-            DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes));
-            for (int i = 0; i < nFloats; i++) {
-                if (dis.available() >= (Float.SIZE / 8)) {
-                    floats[i] = dis.readFloat();
-                }
-            }
-            dis.close();
-            return floats;
-        } catch (IOException ex) {
-            ex.printStackTrace();
-            throw new RuntimeException(ex);
-        }
-    }
-
-    public static int[] convertBytesToInts(byte[] bytes) {
-        try {
-            int nInts = bytes.length / (Integer.SIZE / 8);
-            int[] ints = new int[nInts];
-            DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes));
-            for (int i = 0; i < nInts; i++) {
-                if (dis.available() >= (Integer.SIZE / 8)) {
-                    ints[i] = dis.readInt();
-                }
-            }
-            dis.close();
-            return ints;
-        } catch (IOException ex) {
-            ex.printStackTrace();
-            throw new RuntimeException(ex);
-        }
-    }
-
-    public static long[] convertBytesToLongs(byte[] bytes) {
-        try {
-            int nLongs = bytes.length / (Long.SIZE / 8);
-            long[] longs = new long[nLongs];
-            DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes));
-            for (int i = 0; i < nLongs; i++) {
-                if (dis.available() >= (Long.SIZE / 8)) {
-                    longs[i] = dis.readLong();
-                }
-            }
-            dis.close();
-            return longs;
-        } catch (IOException ex) {
-            ex.printStackTrace();
-            throw new RuntimeException(ex);
-        }
-    }
-
-    /**
-     * Read an array of strings from the data stream. It is assumed that the file contains fixed length ascii arrays
-     * of nChars characters each.
-     */
-    public static String[] convertBytesToStrings(byte[] bytes, int nChars) {
-        try {
-
-            int nStrings = (int) (bytes.length / (nChars * (Character.SIZE / 8)));
-            String[] strings = new String[nStrings];
-            byte[] stringBytes = new byte[nChars * (Character.SIZE / 8)];
-
-            DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes));
-            for (int i = 0; i < nStrings; i++) {
-                if (dis.available() >= (nChars * (Character.SIZE / 8))) {
-                    dis.read(stringBytes);
-                    strings[i] = new String(stringBytes, "UTF-16BE").trim();
-                }
-            }
-            return strings;
-
-        } catch (IOException ex) {
-            ex.printStackTrace();
-            throw new RuntimeException(ex);
-        }
-    }
-
-}
diff --git a/src/org/broad/igv/preprocess/old/ChromosomeFileGroup.java b/src/org/broad/igv/preprocess/old/ChromosomeFileGroup.java
deleted file mode 100644
index 3167143..0000000
--- a/src/org/broad/igv/preprocess/old/ChromosomeFileGroup.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.preprocess.old;
-
-import java.io.File;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Represents the complete group of data files defining a single chromosome.
- *
- * @author jrobinso
- */
-class ChromosomeFileGroup {
-
-    enum LocationType {
-        LONG, INT
-    }
-
-    ;
-
-    LocationType locationType = LocationType.LONG;
-    private File locationFile;
-    private File markerIdFile;
-
-    public ChromosomeFileGroup(LocationType type) {
-        locationType = type;
-    }
-
-    /**
-     * Map of array name -> data file
-     */
-    private Map<String, File> dataFiles = new HashMap();
-
-    public File getLocationFile() {
-        return locationFile;
-    }
-
-    public void setLocationFile(File locationFile) {
-        this.locationFile = locationFile;
-    }
-
-    public File getMarkerIdFile() {
-        return markerIdFile;
-    }
-
-    public void setMarkerIdFile(File markerIdFile) {
-        this.markerIdFile = markerIdFile;
-    }
-
-    public void addDataFile(String array, File dataFile) {
-        dataFiles.put(array, dataFile);
-    }
-
-    public File getDataFile(String array) {
-        return dataFiles.get(array);
-    }
-
-    /**
-     * Assumes all data files are equal length (they have to be), and
-     * are of type float (again, they have to be).
-     */
-    public long getDataLength() {
-        if (dataFiles.isEmpty()) {
-            return 0;
-        }
-        File anyDataFile = dataFiles.values().iterator().next();
-        return anyDataFile.length() / 4;
-    }
-
-    public Collection<String> getArrayNames() {
-        return dataFiles.keySet();
-    }
-
-    public boolean validate() {
-
-        if (locationFile == null) {
-            System.err.println("Missing location file");
-            return false;
-        }
-        if (dataFiles == null || dataFiles.isEmpty()) {
-            System.err.println("No data files for " + locationFile.getName());
-            return false;
-        } else if (!locationFile.exists()) {
-            System.err.println("Location file: " + locationFile.getAbsolutePath() + " does not exist.");
-            return false;
-        } else {
-            long fSize = locationFile.length();
-            int nLocPoints = (locationType == LocationType.LONG ?
-                    (int) (fSize / 8) : (int) (fSize / 4));
-            for (File dataFile : dataFiles.values()) {
-                if (!dataFile.exists()) {
-                    System.err.println("Data file: " + dataFile.getAbsolutePath() + " does not exist.");
-                    return false;
-                }
-                int nDataPts = (int) (dataFile.length() / 4);
-                if (nDataPts != nLocPoints) {
-                    System.err.println("Unexpected file size for file: " + dataFile.getName() + "  expected " +
-                            nLocPoints * 4 + " size = " + dataFile.length());
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-}
diff --git a/src/org/broad/igv/preprocess/old/CollectionBin.java b/src/org/broad/igv/preprocess/old/CollectionBin.java
deleted file mode 100644
index ffecdf0..0000000
--- a/src/org/broad/igv/preprocess/old/CollectionBin.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-package org.broad.igv.preprocess.old;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @author jrobinso
- */
-public class CollectionBin implements Bin {
-
-
-    int start;
-    List<Integer> indeces = new ArrayList();
-
-    public CollectionBin(int start) {
-        this.start = start;
-    }
-
-    public void addIndex(int index) {
-        indeces.add(index);
-    }
-
-    public List<Integer> getIndeces() {
-        return indeces;
-    }
-
-    public int getStart() {
-        return start;
-    }
-
-    public int getFeatureCount() {
-        return indeces.size();
-    }
-
-}
diff --git a/src/org/broad/igv/preprocess/old/FeatureBin.java b/src/org/broad/igv/preprocess/old/FeatureBin.java
deleted file mode 100644
index 73b3ffa..0000000
--- a/src/org/broad/igv/preprocess/old/FeatureBin.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * Bin.java
- *
- * Created on October 29, 2007, 3:40 PM
- *
- * To change this template, choose Tools | Template Manager
- * and open the template in the editor.
- */
-package org.broad.igv.preprocess.old;
-
-import org.broad.igv.feature.LocusScore;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Represents a tile bin.
- * start -- start location of the bin
- * features --  list of features that partially or fully overlap the bin
- * <p/>
- * // TODO = this is a mix of two types, a feature type bin and snp type bin.
- * //        the feature bin uses the "features" collection, snp type uses
- * startIndex / endIndex.
- * //        refactor to combine both or split.
- *
- * @author jrobinso
- */
-public class FeatureBin implements Bin {
-
-    private int start;
-    private List<LocusScore> features;
-
-    public FeatureBin(int location) {
-        this.start = location;
-        features = new ArrayList();
-    }
-
-    public void addFeature(LocusScore feature) {
-        features.add(feature);
-    }
-
-    public List<LocusScore> getFeatures() {
-        return features;
-    }
-
-    public int getFeatureCount() {
-
-        return getFeatures().size();
-
-    }
-
-    /**
-     * Return the scores for this bin after removing all NaN values.  If
-     * there are no scores return null;
-     *
-     * @return
-     */
-    public float[] getFeatureScores() {
-
-        int nScores = 0;
-        for (LocusScore f : features) {
-            if (!Float.isNaN(f.getScore())) {
-                nScores++;
-            }
-        }
-
-        if (nScores > 0) {
-            float[] scores = new float[nScores];
-            int scoreIndex = 0;
-            for (LocusScore f : features) {
-                float s = f.getScore();
-                if (!Float.isNaN(s)) {
-                    scores[scoreIndex] = s;
-                    scoreIndex++;
-                }
-            }
-            return scores;
-        } else {
-            return null;
-        }
-    }
-
-    public int getStart() {
-        return start;
-    }
-}
-
diff --git a/src/org/broad/igv/preprocess/old/FeatureBinCalculator.java b/src/org/broad/igv/preprocess/old/FeatureBinCalculator.java
deleted file mode 100644
index 21104de..0000000
--- a/src/org/broad/igv/preprocess/old/FeatureBinCalculator.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.preprocess.old;
-
-import org.broad.igv.feature.LocusScore;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.TreeMap;
-
-/**
- * @author jrobinso
- */
-public class FeatureBinCalculator {
-
-    /**
-     * Allocates features to FeatureBins.  FeatureBins are equally spaced and begin at startLocation.
-     *
-     * @param features
-     * @param nFeatureBins
-     * @param featureBinSize
-     * @param startLocation
-     * @param endLocation
-     * @return
-     */
-    public List<FeatureBin> computeFeatureBins(List<? extends LocusScore> features, int nFeatureBins, double binSize, int startLocation, int endLocation) {
-
-        TreeMap<Integer, FeatureBin> featureBins = new TreeMap();
-
-        // Allocate features to FeatureBins
-        for (LocusScore feature : features) {
-            int binStart = (int) ((feature.getStart() - startLocation) / binSize);
-            int binEnd = (int) ((feature.getEnd() - startLocation) / binSize);
-            if (binEnd >= 0 && binStart < nFeatureBins) {
-                int lastBin = Math.min(binEnd + 1, nFeatureBins);
-                for (int b = Math.max(0, binStart); b < lastBin; b++) {
-                    // We know this is a feature bin.  Ugly, but no time to redesign this
-                    FeatureBin fBin = featureBins.get(b);
-                    if (fBin == null) {
-                        int location = (int) (startLocation + b * binSize);
-                        fBin = new FeatureBin(location);
-                        featureBins.put(b, fBin);
-                    }
-                    fBin.addFeature(feature);
-                }
-            }
-        }
-
-        Collection c = featureBins.values();
-        return new ArrayList(featureBins.values());
-    }
-
-
-
-}
diff --git a/src/org/broad/igv/preprocess/old/IGVPreprocessor.java b/src/org/broad/igv/preprocess/old/IGVPreprocessor.java
deleted file mode 100644
index f1e0c8a..0000000
--- a/src/org/broad/igv/preprocess/old/IGVPreprocessor.java
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.preprocess.old;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import org.apache.log4j.Logger;
-import org.broad.igv.data.*;
-import org.broad.igv.data.ProcessingUtils;
-import org.broad.igv.feature.GenomeManager;
-import org.broad.igv.util.ParsingUtils;
-import org.broad.igv.track.TrackType;
-import org.broad.igv.util.ResourceLocator;
-
-import java.io.*;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/**
- * @author jrobinso
- */
-public class IGVPreprocessor {
-
-    static Logger log = Logger.getLogger(IGVPreprocessor.class);
-
-    /**
-     * Example:
-     * IGVPreprocessor input.file  output.file.h5 spombe
-     *
-     * @param args
-     * @throws Exception
-     */
-    public static void main(String args[]) throws Exception {
-
-        LinkedHashMap<String, String> argsMap = ProcessingUtils.parseArgs(args);
-
-        //initGenomeMap();
-
-        String inputFileName = args[0];
-        String outputFileName = args[1];
-        String genomeId = args[2];
-
-        String genomeListUrl = argsMap.get("-genomeList");
-        initGenomeMap(genomeListUrl);
-
-        boolean regevLab = argsMap.containsKey("-regev");
-        String span = argsMap.get("-windowWidth");
-        String maxZoomLevel = argsMap.get("-maxZoomLevel");
-
-
-        int maxZoom = 6;
-        if ((maxZoomLevel != null) && (maxZoomLevel.length() > 0)) {
-            try {
-                maxZoom = Integer.parseInt(maxZoomLevel);
-            } catch (NumberFormatException numberFormatException) {
-                System.err.println(numberFormatException.getMessage());
-            }
-        }
-
-        String typeString = argsMap.get("-type");
-
-        TrackType type = TrackType.UNKNOWN;
-        if (typeString != null) {
-            try {
-                type = TrackType.valueOf(typeString);
-
-            } catch (Exception exception) {
-            }
-        }
-
-        final String filterString = argsMap.get("-filter");
-
-        //String genomeUrl = genomeUrlMap.get(genomeId);
-        //if (genomeUrl == null) {
-        //    System.err.println("Unknown genome id: " + genomeId);
-        //    System.exit(-1);
-        //}
-
-        GenomeManager.getInstance().findGenomeAndLoad(genomeId);
-
-        File inputFile = new File(inputFileName);
-        File outputFile = new File(outputFileName);
-
-
-        if (inputFile.isDirectory() && !outputFile.isDirectory()) {
-            System.err.println("Input file is a directory but output file is not.");
-            System.exit(-1);
-
-        }
-        if (outputFile.isDirectory() && !inputFile.isDirectory()) {
-            outputFile = new File(outputFile, inputFile.getName() + ".h5");
-        }
-
-
-        if (inputFile.isDirectory()) {
-            for (File iFile : inputFile.listFiles(new FileFilter() {
-
-                public boolean accept(File arg0) {
-                    return filterString == null || arg0.getAbsolutePath().endsWith(filterString);
-                }
-            })) {
-                File oFile = new File(outputFile, iFile.getName() + ".h5");
-                processFile(iFile, oFile, genomeId, type, maxZoom, regevLab);
-            }
-        } else {
-            processFile(inputFile, outputFile, genomeId, type, maxZoom, regevLab);
-        }
-
-    }
-
-    private static void processFile(File inputFile, File outputFile, String genomeId,
-                                    TrackType type, int maxZoom, boolean regevLab) {
-
-        //if (!outputFile.getName().endsWith(".h5"))
-        //{
-        //    outputFile = new File(outputFile.getAbsolutePath() + ".h5");
-        //}
-        String name = inputFile.getName();
-
-        if (regevLab) {
-            System.out.println("<Resource name=\"" + name + " (" + genomeId + ")\"");
-            System.out.println("path=" + "\"" + outputFile.getAbsolutePath() + "\"");
-            System.out.println("serverURL=\"http://www.broadinstitute.org/webservices/igv\"/>");
-        } else {
-            System.out.println("Processing " + inputFile.getAbsolutePath() + " -> " +
-                    outputFile.getAbsolutePath());
-        }
-
-
-        AbstractProcessor processor = null;
-
-        String shortFileName = inputFile.getName().toLowerCase();
-        if (shortFileName.endsWith(".txt") || shortFileName.endsWith(".xls")) {
-            shortFileName = shortFileName.substring(0, shortFileName.lastIndexOf("."));
-        }
-
-        if (shortFileName.endsWith(".gct") || shortFileName.endsWith(".res") ||
-                inputFile.getName().endsWith(".tab")) {
-            processor = processGCT(inputFile, genomeId, name, type);
-
-        } else if (shortFileName.endsWith(".cn") || shortFileName.endsWith(".xcn") || shortFileName.endsWith(".snp")) {
-            processor = processCN(inputFile, genomeId, name, type);
-
-        } else if (shortFileName.endsWith(".igv")) {
-            processor = processIGV(inputFile, genomeId, name, type);
-        } else if (shortFileName.endsWith(".wig")) {
-            String dsName = inputFile.getName().replace(".wig", "");
-            processor = processWIG(dsName, genomeId, inputFile);
-        } else {
-            log.error("Unrecognized input file extension: " + name);
-            System.exit(-1);
-        }
-
-
-        if (processor != null) {
-            processor.setZoomMax(maxZoom);
-            processor.process(outputFile.getAbsolutePath());
-
-        }
-    }
-
-    private static AbstractProcessor processGCT(File inputFile, String genomeId, String name,
-                                                TrackType type) {
-
-        if (type == TrackType.UNKNOWN) {
-            type = TrackType.GENE_EXPRESSION;
-        }
-
-        GCTDatasetParser parser = null;
-        try {
-            parser = new GCTDatasetParser(inputFile, null, genomeId);
-        } catch (IOException e) {
-            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
-            return null;
-        }
-        Dataset ds = null;
-
-        parser.createDataset();
-        ds.setName(name);
-        ((GCTDataset) ds).setType(type);
-        ((GCTDataset) ds).setNormalized(true);
-        ((GCTDataset) ds).setLogValues(true);
-
-        return new OverlappingProcessor(ds, null);
-    }
-
-    private static AbstractProcessor processCN(File inputFile, String genomeId, String name,
-                                               TrackType type) {
-        if (type == TrackType.UNKNOWN) {
-            type = TrackType.COPY_NUMBER;
-        }
-        IGVDataset ds = new IGVDataset(genomeId, inputFile);
-        ds.setName(name);
-        ds.setTrackType(type);
-        return new NonOverlappingProcessor(ds, null);
-    }
-
-    private static AbstractProcessor processIGV(File inputFile, String genomeId, String name,
-                                                TrackType type) {
-        if (type == TrackType.UNKNOWN) {
-            type = TrackType.OTHER;
-        }
-
-        IGVDataset ds = new IGVDataset(genomeId, inputFile);
-        ds.setName(name);
-        ds.setTrackType(type);
-        return new OverlappingProcessor(ds, null);
-    }
-
-    private static AbstractProcessor processCNBin(File inputFile, String genomeId, String name,
-                                                  TrackType type, boolean chipFormat, boolean intLocations, int wSpan,
-                                                  final String filterString) {
-
-
-        FilenameFilter filter = new FilenameFilter() {
-
-            public boolean accept(File dir, String name) {
-                return name.endsWith(".bin") && (!name.contains("_random") && ((filterString == null) || name.contains(filterString)));
-            }
-        };
-
-        Dataset ds2 = null;
-        System.out.println("chipFormat = " + chipFormat);
-        if (chipFormat) {
-            SolexaBinaryDataset ds = new SolexaBinaryDataset(name, type, genomeId,
-                    inputFile.getAbsolutePath(), filter);
-            ds.setWindowSpan(wSpan);
-            ds.setNormalized(false);
-            ds2 = ds;
-        } else {
-            SnpBinaryDataset ds = new SnpBinaryDataset(name, type, genomeId,
-                    inputFile.getAbsolutePath(), filter, chipFormat,
-                    intLocations);
-            ds.setWindowSpan(wSpan);
-            ds.setNormalized(false);
-            ds2 = ds;
-        }
-
-        for (String nm : ds2.getDataHeadings()) {
-            System.out.println(nm);
-        }
-
-        return new NonOverlappingProcessor(ds2);
-    }
-
-    private static AbstractProcessor processWIG(String dsName, String genomeId, File inputFile) {
-
-        AbstractProcessor processor;
-        Dataset ds = (new WiggleParser(new ResourceLocator(inputFile.getAbsolutePath()),
-                genomeId)).parse();
-        ds.setName(dsName);
-        processor = new OverlappingProcessor(ds, null);
-
-        return processor;
-    }
-
-    static Map<String, String> genomeUrlMap;
-
-    private static void initGenomeMap(String genomeListUrl) {
-        genomeUrlMap = new HashMap();
-        BufferedReader reader = null;
-        try {
-            if (genomeListUrl == null) {
-                genomeListUrl = "http://www.broadinstitute.org/igv/resources/genomes/genomes.txt";
-            }
-
-            reader = ParsingUtils.openBufferedReader(genomeListUrl);
-
-            String nextLine = null;
-            while ((nextLine = reader.readLine()) != null) {
-                String[] tokens = nextLine.split("\t");
-                if (tokens.length >= 2) {
-                    String url = tokens[1].trim();
-                    String key = tokens[2].trim();
-                    genomeUrlMap.put(key, url);
-                }
-            }
-        } catch (IOException ex) {
-            log.error("Error initializing genome map.", ex);
-            System.exit(-1);
-        } finally {
-            try {
-                reader.close();
-            } catch (IOException ex) {
-                log.error("Error closing genome list reader", ex);
-            }
-        }
-    }
-
-    private static void initGenomeMap() {
-        genomeUrlMap = new HashMap();
-        BufferedReader reader = null;
-        try {
-
-            URL genomeListURL = new URL("http://www.broadinstitute.org/igv/resources/genomes/genomes.txt");
-            URLConnection connection = genomeListURL.openConnection();
-            reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
-
-            String nextLine = null;
-            while ((nextLine = reader.readLine()) != null) {
-                String[] tokens = nextLine.split("\t");
-                if (tokens.length > 2) {
-                    String url = tokens[1].trim();
-                    String key = tokens[2].trim();
-                    genomeUrlMap.put(key, url);
-                }
-
-            }
-        } catch (IOException ex) {
-            System.err.println("Error initializing genome map.");
-            ex.printStackTrace();
-            System.exit(-1);
-        } finally {
-            try {
-                reader.close();
-            } catch (IOException ex) {
-            }
-        }
-    }
-
-}
diff --git a/src/org/broad/igv/preprocess/old/LEDataInputStream.java b/src/org/broad/igv/preprocess/old/LEDataInputStream.java
deleted file mode 100644
index 89e47c2..0000000
--- a/src/org/broad/igv/preprocess/old/LEDataInputStream.java
+++ /dev/null
@@ -1,282 +0,0 @@
-package org.broad.igv.preprocess.old;
-
-/**
- * LEDataInputStream.java
- * copyright (c) 1998-2007 Roedy Green, Canadian Mind* Products
- * Very similar to DataInputStream except it reads
- * little-endian instead of big-endian binary data. We can't extend
- * DataInputStream directly since it has only final methods, though
- * DataInputStream itself is not final. This forces us implement
- * LEDataInputStream with a DataInputStream object, and use wrapper methods.
- */
-
-import java.io.DataInput;
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * reads little endian binary data .
- */
-public final class LEDataInputStream implements DataInput {
-
-    // ------------------------------ FIELDS ------------------------------
-
-    /**
-     * undisplayed copyright notice.
-     *
-     * @noinspection UnusedDeclaration
-     */
-    private static final String EMBEDDEDCOPYRIGHT =
-            "copyright (c) 1999-2007 Roedy Green, Canadian Mind Products, http://mindprod.com";
-
-    /**
-     * to get at the big-Endian methods of a basic DataInputStream
-     *
-     * @noinspection WeakerAccess
-     */
-    protected final DataInputStream dis;
-
-    /**
-     * to get at the a basic readBytes method.
-     *
-     * @noinspection WeakerAccess
-     */
-    protected final InputStream is;
-
-    /**
-     * work array for buffering input.
-     *
-     * @noinspection WeakerAccess
-     */
-    protected final byte[] work;
-
-    // -------------------------- PUBLIC STATIC METHODS --------------------------
-
-    /**
-     * Note. This is a STATIC method!
-     *
-     * @param in stream to read UTF chars from (endian irrelevant)
-     * @return string from stream
-     * @throws IOException if read fails.
-     */
-    public static String readUTF(DataInput in) throws IOException {
-        return DataInputStream.readUTF(in);
-    }
-
-    // -------------------------- PUBLIC INSTANCE  METHODS --------------------------
-    /**
-     * constructor.
-     *
-     * @param in binary inputstream of little-endian data.
-     */
-    public LEDataInputStream(InputStream in) {
-        this.is = in;
-        this.dis = new DataInputStream(in);
-        work = new byte[8];
-    }
-
-    /**
-     * close.
-     *
-     * @throws IOException if close fails.
-     */
-    public final void close() throws IOException {
-        dis.close();
-    }
-
-    /**
-     * Read bytes. Watch out, read may return fewer bytes than requested.
-     *
-     * @param ba  where the bytes go.
-     * @param off offset in buffer, not offset in file.
-     * @param len count of bytes to read.
-     * @return how many bytes read.
-     * @throws IOException if read fails.
-     */
-    public final int read(byte ba[], int off, int len) throws IOException {
-        // For efficiency, we avoid one layer of wrapper
-        return is.read(ba, off, len);
-    }
-
-    /**
-     * read only a one-byte boolean.
-     *
-     * @return true or false.
-     * @throws IOException if read fails.
-     * @see java.io.DataInput#readBoolean()
-     */
-    public final boolean readBoolean() throws IOException {
-        return dis.readBoolean();
-    }
-
-    /**
-     * read byte.
-     *
-     * @return the byte read.
-     * @throws IOException if read fails.
-     * @see java.io.DataInput#readByte()
-     */
-    public final byte readByte() throws IOException {
-        return dis.readByte();
-    }
-
-    /**
-     * Read on char. like DataInputStream.readChar except little endian.
-     *
-     * @return little endian 16-bit unicode char from the stream.
-     * @throws IOException if read fails.
-     */
-    public final char readChar() throws IOException {
-        dis.readFully(work, 0, 2);
-        return (char) ((work[1] & 0xff) << 8 | (work[0] & 0xff));
-    }
-
-    /**
-     * Read a double. like DataInputStream.readDouble except little endian.
-     *
-     * @return little endian IEEE double from the datastream.
-     * @throws IOException
-     */
-    public final double readDouble() throws IOException {
-        return Double.longBitsToDouble(readLong());
-    }
-
-    /**
-     * Read one float. Like DataInputStream.readFloat except little endian.
-     *
-     * @return little endian IEEE float from the datastream.
-     * @throws IOException if read fails.
-     */
-    public final float readFloat() throws IOException {
-        return Float.intBitsToFloat(readInt());
-    }
-
-    /**
-     * Read bytes until the array is filled.
-     *
-     * @see java.io.DataInput#readFully(byte[])
-     */
-    public final void readFully(byte ba[]) throws IOException {
-        dis.readFully(ba, 0, ba.length);
-    }
-
-    /**
-     * Read bytes until the count is satisfied.
-     *
-     * @throws IOException if read fails.
-     * @see java.io.DataInput#readFully(byte[],int,int)
-     */
-    public final void readFully(byte ba[],
-                                int off,
-                                int len) throws IOException {
-        dis.readFully(ba, off, len);
-    }
-
-    /**
-     * Read an int, 32-bits. Like DataInputStream.readInt except little endian.
-     *
-     * @return little-endian binary int from the datastream
-     * @throws IOException if read fails.
-     */
-    public final int readInt() throws IOException {
-        dis.readFully(work, 0, 4);
-        return (work[3]) << 24
-                | (work[2] & 0xff) << 16
-                | (work[1] & 0xff) << 8
-                | (work[0] & 0xff);
-    }
-
-    /**
-     * Read a line.
-     *
-     * @return a rough approximation of the 8-bit stream as a 16-bit unicode
-     *         string
-     * @throws IOException
-     * @noinspection deprecation
-     * @deprecated This method does not properly convert bytes to characters.
-     *             Use a Reader instead with a little-endian encoding.
-     */
-    public final String readLine() throws IOException {
-        return dis.readLine();
-    }
-
-    /**
-     * read a long, 64-bits.  Like DataInputStream.readLong except little
-     * endian.
-     *
-     * @return little-endian binary long from the datastream.
-     * @throws IOException
-     */
-    public final long readLong() throws IOException {
-        dis.readFully(work, 0, 8);
-        return (long) (work[7]) << 56
-                |
-                /* long cast needed or shift done modulo 32 */
-                (long) (work[6] & 0xff) << 48
-                | (long) (work[5] & 0xff) << 40
-                | (long) (work[4] & 0xff) << 32
-                | (long) (work[3] & 0xff) << 24
-                | (long) (work[2] & 0xff) << 16
-                | (long) (work[1] & 0xff) << 8
-                | (long) (work[0] & 0xff);
-    }
-
-    /**
-     * Read short, 16-bits. Like DataInputStream.readShort except little
-     * endian.
-     *
-     * @return little endian binary short from stream.
-     * @throws IOException if read fails.
-     */
-    public final short readShort() throws IOException {
-        dis.readFully(work, 0, 2);
-        return (short) ((work[1] & 0xff) << 8 | (work[0] & 0xff));
-    }
-
-    /**
-     * Read UTF counted string.
-     *
-     * @return String read.
-     */
-    public final String readUTF() throws IOException {
-        return dis.readUTF();
-    }
-
-    /**
-     * Read an unsigned byte. Note: returns an int, even though says Byte
-     * (non-Javadoc)
-     *
-     * @throws IOException if read fails.
-     * @see java.io.DataInput#readUnsignedByte()
-     */
-    public final int readUnsignedByte() throws IOException {
-        return dis.readUnsignedByte();
-    }
-
-    /**
-     * Read an unsigned short, 16 bits. Like DataInputStream.readUnsignedShort
-     * except little endian. Note, returns int even though it reads a short.
-     *
-     * @return little-endian int from the stream.
-     * @throws IOException if read fails.
-     */
-    public final int readUnsignedShort() throws IOException {
-        dis.readFully(work, 0, 2);
-        return ((work[1] & 0xff) << 8 | (work[0] & 0xff));
-    }
-
-    /**
-     * Skip over bytes in the stream. See the general contract of the
-     * <code>skipBytes</code> method of <code>DataInput</code>.
-     * <p/>
-     * Bytes for this operation are read from the contained input stream.
-     *
-     * @param n the number of bytes to be skipped.
-     * @return the actual number of bytes skipped.
-     * @throws IOException if an I/O error occurs.
-     */
-    public final int skipBytes(int n) throws IOException {
-        return dis.skipBytes(n);
-    }
-}
diff --git a/src/org/broad/igv/preprocess/old/NonOverlappingProcessor.java b/src/org/broad/igv/preprocess/old/NonOverlappingProcessor.java
deleted file mode 100644
index d7ffdf6..0000000
--- a/src/org/broad/igv/preprocess/old/NonOverlappingProcessor.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/****************************************************************************
- * NCSA HDF                                                                 *
- * National Comptational Science Alliance                                   *
- * University of Illinois at Urbana-Champaign                               *
- * 605 E. Springfield, Champaign IL 61820                                   *
- *                                                                          *
- * For conditions of distribution and use, see the accompanying             *
- * hdf-java/COPYING file.                                                   *
- *                                                                          *
- ****************************************************************************/
-package org.broad.igv.preprocess.old;
-
-//import org.broad.igv.data.FeatureDatasetParser;
-//import org.broad.igv.data.FeatureDataset;
-
-import org.apache.log4j.Logger;
-import org.broad.igv.data.Dataset;
-import org.broad.igv.tools.StatusMonitor;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Processes a dataset with no overlapping features.  Bins can be allocated
- * more efficiently than overlapping feature datasets.
- */
-public class NonOverlappingProcessor extends AbstractProcessor {
-
-    static int maxNumberWarnings = 200;
-
-    static int numberWarnings = 0;
-
-    Logger log = Logger.getLogger(NonOverlappingProcessor.class);
-
-    boolean chipFormat = false;
-
-    public NonOverlappingProcessor(Dataset ds, StatusMonitor sm) {
-        super(ds, sm);
-    }
-
-    public NonOverlappingProcessor(Dataset ds) {
-        super(ds);
-    }
-
-    /**
-     * Allocate rows from the dataset to bins.
-     *
-     * @param chr
-     * @param nBins
-     * @param binSize
-     * @return
-     */
-    protected List<Bin> allocateBins(String chr, int nBins, double binSize,
-                                     int[] startLocations, int[] ignore) {
-
-        List<RangeBin> allBins = new ArrayList(nBins);
-        for (int i = 0; i < nBins; i++) {
-            int location = (int) (i * binSize);
-            allBins.add(new RangeBin(location));
-        }
-
-        RangeBin lastBin = null;
-        for (int i = 0; i < startLocations.length; i++) {
-            int start = (int) startLocations[i];
-            int bStart = (int) (start / binSize);
-            if (bStart >= allBins.size()) {
-                if (numberWarnings < maxNumberWarnings) {
-                    log.info("Start location " + chr + ": " + start + " exceeds expected length.  Wrong genome?");
-                    numberWarnings++;
-                }
-            } else {
-                RangeBin bin = allBins.get(bStart);
-                if (lastBin == null) {
-                    bin.setStartIndex(i);
-                } else if (bin != lastBin) {
-                    bin.setStartIndex(i);
-                    lastBin.setEndIndex(i);
-                }
-                lastBin = bin;
-            }
-        }
-        // Set the end index of the last bin if unset.
-        if (lastBin != null && lastBin.getEndIndex() <= 0) {
-            lastBin.setEndIndex(startLocations.length);
-        }
-
-        // Remove empty bins.
-        List<Bin> occupiedBins = new ArrayList(allBins.size());
-        for (RangeBin bin : allBins) {
-            if (bin.getFeatureCount() > 0) {
-                occupiedBins.add(bin);
-            }
-        }
-
-        return occupiedBins;
-    }
-
-    /**
-     * Return the subset of data from the data array that is allocated to
-     * this bin.
-     *
-     * @param data
-     * @param bin
-     * @return
-     */
-    protected float[] getDataForBin(float[] data, Bin bin) {
-
-        RangeBin rBin = (RangeBin) bin;
-
-        // Specifc  -> Get data for bin
-        int nDataPts = 0;
-        for (int i = rBin.getStartIndex(); i < rBin.getEndIndex() && i < data.length; i++) {
-            if (!Float.isNaN(data[i])) {
-                nDataPts++;
-            }
-        }
-
-        if (nDataPts > 0) {
-            float[] binData = new float[nDataPts];
-            int binDataIndex = 0;
-            for (int i = rBin.getStartIndex(); i < rBin.getEndIndex() && binDataIndex < binData.length; i++) {
-                if (!Float.isNaN(data[i])) {
-                    binData[binDataIndex] = data[i];
-                    binDataIndex++;
-                }
-            }
-            //
-            return binData;
-        } else {
-            return null;
-        }
-    }
-}
diff --git a/src/org/broad/igv/preprocess/old/OverlappingProcessor.java b/src/org/broad/igv/preprocess/old/OverlappingProcessor.java
deleted file mode 100644
index 6f45e8f..0000000
--- a/src/org/broad/igv/preprocess/old/OverlappingProcessor.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/****************************************************************************
- * NCSA HDF                                                                 *
- * National Comptational Science Alliance                                   *
- * University of Illinois at Urbana-Champaign                               *
- * 605 E. Springfield, Champaign IL 61820                                   *
- *                                                                          *
- * For conditions of distribution and use, see the accompanying             *
- * hdf-java/COPYING file.                                                   *
- *                                                                          *
- ****************************************************************************/
-package org.broad.igv.preprocess.old;
-
-import org.apache.log4j.Logger;
-import org.broad.igv.data.Dataset;
-import org.broad.igv.tools.StatusMonitor;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Processor for features which might have overlapping loci.  For example,
- * gene expression.
- */
-public class OverlappingProcessor extends AbstractProcessor {
-
-    static Logger log = Logger.getLogger(OverlappingProcessor.class);
-
-    public OverlappingProcessor(Dataset ds, StatusMonitor sm) {
-        super(ds, sm);
-    }
-
-    public OverlappingProcessor(Dataset ds) {
-        super(ds);
-    }
-
-    /**
-     * Allocate rows from the dataset to bins.
-     *
-     * @param chr
-     * @param nBins
-     * @param binSize
-     * @return
-     */
-    protected List<Bin> allocateBins(String chr, int nBins, double binSize,
-                                     int[] startLocations, int[] endLocations) {
-
-        assert startLocations.length == endLocations.length;
-
-        List<CollectionBin> allBins = new ArrayList(nBins);
-        for (int i = 0; i < nBins; i++) {
-            int location = (int) (i * binSize);
-            allBins.add(new CollectionBin(location));
-        }
-
-        for (int i = 0; i < startLocations.length; i++) {
-            int bStart = (int) (startLocations[i] / binSize);
-            int end = (endLocations == null ? startLocations[i] + 1 : endLocations[i]);
-            int bEnd = (int) (end / binSize);
-
-            if (bStart < 0) {
-                System.out.println("Negative bStart");
-            }
-
-            for (int b = bStart; b <= bEnd; b++) {
-                if (b < allBins.size()) {
-                    allBins.get(b).addIndex(i);
-                } else {
-                    //log.info("Feature location > chr length (" + b * binSize + ") " + chr);
-                }
-            }
-
-
-        }
-
-
-        // Remove empty bins.
-        List<Bin> occupiedBins = new ArrayList(allBins.size());
-        for (CollectionBin bin : allBins) {
-            if (bin.getFeatureCount() > 0) {
-                occupiedBins.add(bin);
-            }
-        }
-
-        return occupiedBins;
-    }
-
-    /**
-     * Return data for the specified bin.  All NaN values are removed.
-     */
-    protected float[] getDataForBin(float[] data, Bin bin) {
-
-        CollectionBin cBin = (CollectionBin) bin;
-        // Specifc  -> Get data for bin
-        // Specifc  -> Get data for bin
-        int nDataPts = 0;
-        for (Integer i : cBin.getIndeces()) {
-            if (!Float.isNaN(data[i])) {
-                nDataPts++;
-            }
-        }
-
-        if (nDataPts > 0) {
-            float[] binData = new float[nDataPts];
-            int binDataIndex = 0;
-            for (Integer i : cBin.getIndeces()) {
-                if (!Float.isNaN(data[i])) {
-                    binData[binDataIndex] = data[i];
-                    binDataIndex++;
-                }
-            }
-            return binData;
-        } else {
-            return null;
-        }
-    }
-
-
-}
diff --git a/src/org/broad/igv/preprocess/old/ProbeListCreator.java b/src/org/broad/igv/preprocess/old/ProbeListCreator.java
deleted file mode 100644
index 6722ca2..0000000
--- a/src/org/broad/igv/preprocess/old/ProbeListCreator.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-package org.broad.igv.preprocess.old;
-
-import org.broad.igv.feature.Feature;
-import org.broad.igv.feature.FeatureDB;
-import org.broad.igv.feature.GeneManager;
-import org.broad.igv.feature.UCSCGeneTableParser;
-import org.broad.igv.ui.MiscStuff;
-import org.broad.igv.util.AsciiLineReader;
-
-import java.io.*;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Utlity to create a probe list from an affy annotation file
- *
- * @author jrobinso
- */
-public class ProbeListCreator {
-
-    static String genome = "hg15";
-    static File symbolFile = new File("/Volumes/xchip_tcga/gbm/visualization/MedPop/matched.brain.gi.symbol.2");
-    static File outputFile = new File("/Volumes/xchip_tcga/gbm/visualization/MedPop/giprobes." + genome + ".tab");
-    static File unfoundGenesFile = new File("/Volumes/xchip_tcga/gbm/visualization/MedPop/nogenes" + genome + "tab");
-
-    public static void main(String[] args) throws IOException {
-        createProbeMappingFile();
-    }
-
-    public static void createProbeMappingFile() throws IOException {
-
-
-        GeneManager geneData = new GeneManager(genome, "Gene");
-        String refGeneFile = genome + "_refFlat.txt";
-        InputStream is2 = MiscStuff.class.getResourceAsStream("/resources/" + refGeneFile);
-        AsciiLineReader reader2 = new AsciiLineReader(is2);
-        List<Feature> genes = (new UCSCGeneTableParser(UCSCGeneTableParser.Type.REFFLAT)).loadFeatures(reader2);
-        for (Feature gene : genes) {
-            geneData.addGene(gene);
-        }
-        geneData.sortGeneLists();
-        reader2.close();
-
-        Map<String, String> probeToGene = loadProbeToGeneMap(symbolFile);
-
-        PrintWriter pw2 = new PrintWriter(new FileWriter(unfoundGenesFile));
-        PrintWriter pw = new PrintWriter(new FileWriter(outputFile));
-        for (Map.Entry<String, String> entry : probeToGene.entrySet()) {
-            String probe = entry.getKey();
-            String geneName = entry.getValue();
-            Feature gene = FeatureDB.getFeature(geneName);
-            if (gene == null) {
-                pw2.println(probe + "\t" + geneName);
-            } else {
-                pw.println(probe + "\t" + gene.getChromosome() + "\t" +
-                        gene.getStart() + "\t" + gene.getEnd());
-            }
-        }
-        pw.close();
-    }
-
-    public static Map<String, String> loadProbeToGeneMap(File file) throws FileNotFoundException, IOException {
-
-        BufferedReader reader = new BufferedReader(new FileReader(file));
-
-        LinkedHashMap<String, String> map = new LinkedHashMap();
-        String nextLine = null;
-        while ((nextLine = reader.readLine()) != null) {
-            String[] tokens = nextLine.split("\\s");
-            map.put(tokens[0].trim(), tokens[1].trim());
-        }
-        return map;
-
-    }
-
-
-}
-
diff --git a/src/org/broad/igv/preprocess/old/ProbeListParser.java b/src/org/broad/igv/preprocess/old/ProbeListParser.java
deleted file mode 100644
index 4c1adac..0000000
--- a/src/org/broad/igv/preprocess/old/ProbeListParser.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * ResFileParser.java
- *
- * Created on October 18, 2007, 2:33 PM
- *
- * To change this template, choose Tools | Template Manager
- * and open the template in the editor.
- */
-package org.broad.igv.preprocess.old;
-
-import org.broad.igv.data.ExpressionProbe;
-import org.broad.igv.data.ProbeSet;
-
-import java.io.*;
-import java.util.List;
-
-/**
- * @author jrobinso
- */
-public class ProbeListParser {
-
-    /**
-     * Creates a new instance of ResFileParser
-     */
-    public ProbeListParser() {
-    }
-
-    public static ProbeSet parseFile(File probeFile) {
-
-
-        ProbeSet probeSet = new ProbeSet();
-        try {
-
-            BufferedReader reader = new BufferedReader(new FileReader(probeFile));
-
-            // Skip first row
-            reader.readLine();
-
-            String nextLine = null;
-            while ((nextLine = reader.readLine()) != null) {
-                String[] tokens = nextLine.split("\t");
-                String probeId = tokens[0];
-                String chr = tokens[1].trim();
-                if (chr.startsWith("chr") && !chr.contains("random")) {
-                    int start = Integer.parseInt(tokens[2]);
-                    int end = Integer.parseInt(tokens[3]);
-
-                    ExpressionProbe probe = new ExpressionProbe(probeId, probeId);
-
-                    probe.setChr(chr);
-                    probe.setStart(start);
-                    probe.setEnd(end);
-                    probeSet.add(probe);
-                }
-            }
-            probeSet.sortProbeLists();
-
-        } catch (FileNotFoundException ex) {
-            ex.printStackTrace();
-        } catch (IOException ex) {
-            ex.printStackTrace();
-        }
-        return probeSet;
-
-    }
-
-    // TODO -- make this a unit test
-    public static void main(String[] args) {
-        String fn = "data/expression/WETime.rma.mapped";
-        File file = new File(fn);
-        System.out.println(file.getAbsolutePath());
-        ProbeSet ds = parseFile(file);
-
-        List<ExpressionProbe> probeList = ds.getProbes("chr1");
-        for (int i = 0; i < 10; i++) {
-            System.out.println(probeList.get(i));
-        }
-    }
-}
diff --git a/src/org/broad/igv/preprocess/old/ProcessingUtils.java b/src/org/broad/igv/preprocess/old/ProcessingUtils.java
deleted file mode 100644
index aa0a4c7..0000000
--- a/src/org/broad/igv/preprocess/old/ProcessingUtils.java
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.preprocess.old;
-
-import cern.colt.list.DoubleArrayList;
-import cern.jet.stat.quantile.DoubleQuantileFinder;
-import cern.jet.stat.quantile.QuantileFinderFactory;
-import org.broad.igv.data.DataStatistics;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.LinkedHashMap;
-
-/**
- * @author jrobinso
- */
-public class ProcessingUtils {
-
-    public ProcessingUtils() {
-    }
-
-    /**
-     * locations - sorted array of genomic locations (base pairs)
-     * chrLength - total chrLength in genomic coordinates (base pairs)
-     * nBins - the number of bins to create.
-     */
-    /*public List<Bin> computeBins(long[] locations, int nBins, double binSize, long startLocation, long endLocation) {
-    List<Bin> allBins = createBins(nBins, startLocation, binSize);
-    // Allocate features to bins
-    for (int i = 0; i < locations.length; i++) {
-    int bin = (int) Math.min(allBins.size() - 1, (locations[i] - startLocation) / binSize);
-    if (bin > 0 && bin < allBins.size()) {
-    Feature f = new LocusScore()
-    allBins.get(bin).addFeature(feature);
-    }
-    }
-    List<Bin> occupiedBins = removeEmptyBins(allBins);
-    return occupiedBins;
-    }
-     */
-    /**
-     * Compute some statistics for the input data
-     * TODO move to a utility class
-     * TODO take statis desired as input
-     *
-     * @param data
-     * @return
-     */
-    public DataStatistics computeStats(float[] data) {
-
-        // Check for null or missing data.  Float.NaN is interpreted
-        // as a placeholder for "no data.
-
-        boolean noData = true;
-        if (data != null && data.length > 0) {
-            for (int i = 0; i < data.length; i++) {
-                if (!Float.isNaN(data[i])) {
-                    noData = false;
-                    break;
-                }
-            }
-        }
-
-
-        if (noData) {
-            return DataStatistics.nullDataStat;
-        } else {
-
-            DataStatistics stats = new DataStatistics();
-
-            // Check for array of NaNs
-            DoubleArrayList al = new DoubleArrayList(2);
-            al.add(0.1);
-            al.add(0.5);
-            al.add(0.9);
-            al.add(0.98);
-            DoubleQuantileFinder qf =
-                    QuantileFinderFactory.newDoubleQuantileFinder(true, data.length,
-                            0.001, 0.001, al.size(), null);
-
-
-            double minStat = Double.MAX_VALUE;
-            double maxStat = -Double.MAX_VALUE;
-            double meanStat = 0;
-            for (int i = 0; i < data.length; i++) {
-                if (isValidData(data[i])) {
-                    qf.add(data[i]);
-                    minStat = Math.min(minStat, data[i]);
-                    maxStat = Math.max(maxStat, data[i]);
-                    meanStat += data[i];
-                }
-            }
-
-            if (qf.size() > 0) {
-
-                meanStat /= qf.size();
-                double err2 = 0;
-                for (int i = 0; i < data.length; i++) {
-                    if (isValidData(data[i])) {
-                        err2 += (data[i] - meanStat) * (data[i] - meanStat);
-                    }
-                }
-                double stdDev = Math.sqrt(err2 / qf.size());
-
-                DoubleArrayList quantiles2 = qf.quantileElements(al);
-                stats.setMin(minStat);
-                stats.setMax(maxStat);
-                stats.setMean(meanStat);
-                stats.setPercentile10(quantiles2.get(0));
-                stats.setMedian(quantiles2.get(1));
-                stats.setPercentile90(quantiles2.get(2));
-                stats.setPercentile98(quantiles2.get(3));
-                stats.setStdDev(stdDev);
-            }
-
-            return stats;
-
-        }
-    }
-
-    /**
-     * @param data
-     * @return
-     */
-    public double computeMedian(float[] data) {
-
-        DataStatistics stats = new DataStatistics();
-
-
-        if (data.length > 0) {
-            DoubleArrayList al = new DoubleArrayList();
-            al.add(0.5);
-            DoubleQuantileFinder qf =
-                    QuantileFinderFactory.newDoubleQuantileFinder(true, data.length,
-                            0.001, 0.001, al.size(), null);
-
-
-            for (int i = 0; i < data.length; i++) {
-                if (isValidData(data[i])) {
-                    qf.add(data[i]);
-
-                }
-            }
-
-            if (qf.size() > 0) {
-
-                DoubleArrayList quantiles2 = qf.quantileElements(al);
-                return quantiles2.get(0);
-            }
-        }
-        return 0;
-    }
-
-    private boolean isValidData(float data) {
-        //if (chipFormat) {
-        //    return !Float.isNaN(data) && (data >= 0);
-        //} else {
-        return !Float.isNaN(data);
-        //}
-    }
-
-    /**
-     * Dump the first N data values from the given file as a java formatted
-     * array.  Used for debugging
-     * and testing.
-     *
-     * @param data
-     * @return
-     */
-    public static void dump(File file, Class type, int nPoints) {
-
-        BinaryReader reader = new BinaryReader();
-
-        System.out.print("{");
-        if (type == Long.class) {
-            long[] values = reader.readLongs(file);
-            for (int i = 0; i < nPoints; i++) {
-                System.out.print(values[i]);
-                if (i < nPoints - 1) {
-                    System.out.print(", ");
-                }
-            }
-            System.out.println("}");
-        } else if (type == Float.class) {
-            float[] values = reader.readFloats(file);
-            for (int i = 0; i < nPoints; i++) {
-                System.out.print(values[i]);
-                if (i < nPoints - 1) {
-                    System.out.print(", ");
-                }
-            }
-            System.out.println("}");
-        }
-    }
-
-    /**
-     * Count the number of points between start and end location.  Used
-     * for debugging.
-     * and testing.
-     *
-     * @param data
-     * @return
-     */
-    public static void countPoints(File file, long startLocation, long endLocation) {
-
-        BinaryReader reader = new BinaryReader();
-        long[] values = reader.readLongs(file);
-        int count = 0;
-        for (int i = 0; i < values.length; i++) {
-            if (values[i] >= endLocation) {
-                break;
-            }
-            if (values[i] >= startLocation) {
-                count++;
-            }
-        }
-        System.out.println("Count= " + count);
-    }
-
-    public static void dumpPoints(File file, long startLocation, long endLocation) {
-
-        PrintWriter pw = null;
-        try {
-            pw = new PrintWriter(new FileWriter("feature_dump_gt.txt"));
-            BinaryReader reader = new BinaryReader();
-            long[] values = reader.readLongs(file);
-            for (int i = 0; i < values.length; i++) {
-                if (values[i] >= endLocation) {
-                    break;
-                }
-                if (values[i] >= startLocation) {
-                    pw.println(values[i]);
-
-                }
-            }
-        } catch (IOException ex) {
-            ex.printStackTrace();
-        } finally {
-            pw.close();
-
-        }
-    }
-
-    public static LinkedHashMap<String, String> parseArgs(String[] args) {
-        LinkedHashMap<String, String> argMap = new LinkedHashMap();
-
-        for (int i = 0; i < args.length; i++) {
-            System.out.println(args[i]);
-
-            String key = args[i];
-
-            if (args[i].startsWith("-")) {
-                if (i < args.length - 1) {
-                    i++;
-
-                    if (args[i].startsWith("-")) {
-                        argMap.put(key, "");
-                        i--;
-                    } else {
-                        argMap.put(key, args[i]);
-                    }
-                } else {
-                    argMap.put(key, "");
-                }
-            } else {
-                argMap.put(key, key);
-            }
-        }
-
-        return argMap;
-    }
-
-    public static void main(String[] args) {
-        long startLocation = 4568550;
-        long endLocation = 4611950;
-        dumpPoints(new File("test/data/es/chrX.snplocation.bin"), startLocation, endLocation);
-        //dump(new File("test/data/es/chrX.snplocation.bin"), Long.class, 20);
-        //dump(new File("test/data/es/chrX.ES.K27.bin"), Float.class, 20);
-    }
-}
diff --git a/src/org/broad/igv/preprocess/old/RangeBin.java b/src/org/broad/igv/preprocess/old/RangeBin.java
deleted file mode 100644
index 0a13f07..0000000
--- a/src/org/broad/igv/preprocess/old/RangeBin.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * RangeBin.java
- *
- * Created on October 29, 2007, 3:40 PM
- *
- * To change this template, choose Tools | Template Manager
- * and open the template in the editor.
- */
-package org.broad.igv.preprocess.old;
-
-/**
- * Represents a tile bin.
- * start -- start location of the bin
- * features --  list of features that partially or fully overlap the bin
- * <p/>
- * // TODO = this is a mix of two types, a feature type bin and snp type bin.
- * //        the feature bin uses the "features" collection, snp type uses
- * startIndex / endIndex.
- * //        refactor to combine both or split.
- *
- * @author jrobinso
- */
-public class RangeBin implements Bin {
-
-    private int startIndex;  // <- inclusive
-    private int endIndex;   // <- exclusive
-    private int start;
-
-
-    public RangeBin(int location) {
-        this.start = location;
-    }
-
-
-    public int getFeatureCount() {
-
-        return endIndex - startIndex;
-
-    }
-
-
-    public int getStart() {
-        return start;
-    }
-
-
-    public int getStartIndex() {
-        return startIndex;
-    }
-
-    public int getEndIndex() {
-        return endIndex;
-    }
-
-    public void setStartIndex(int startIndex) {
-        this.startIndex = startIndex;
-    }
-
-    public void setEndIndex(int endIndex) {
-        this.endIndex = endIndex;
-    }
-
-
-}
-
diff --git a/src/org/broad/igv/preprocess/old/SnpBinaryDataset.java b/src/org/broad/igv/preprocess/old/SnpBinaryDataset.java
deleted file mode 100644
index 87d2dab..0000000
--- a/src/org/broad/igv/preprocess/old/SnpBinaryDataset.java
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * Dataset.java
- *
- * Created on October 29, 2007, 11:29 PM
- *
- * To change this template, choose Tools | Template Manager
- * and open the template in the editor.
- */
-package org.broad.igv.preprocess.old;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import org.broad.igv.data.Dataset;
-import org.broad.igv.track.TrackProperties;
-import org.broad.igv.track.TrackType;
-import org.broad.igv.util.ObjectCache;
-
-import java.io.File;
-import java.io.FilenameFilter;
-import java.util.*;
-
-/**
- * (new SnpBinaryProcessor(name, ds, genome, chipFormat, wSpan, norm)).process(outputFile);
- *
- * @author jrobinso
- */
-public class SnpBinaryDataset implements Dataset {
-
-    private String name;
-
-    private TrackType type = TrackType.COPY_NUMBER;
-
-    private boolean solexaFormat = false;
-
-    private String genome;
-
-    private int wSpan = 1;
-
-    private boolean normalized = false;
-
-    /**
-     * Temp variable to cache dataCache array for a single chromosome.
-     */
-
-    // ObjectCache<String, float[]> dataCache;
-    ObjectCache<String, int[]> locations = new ObjectCache();
-
-    private boolean intLocations = false;
-
-    String sourceDirectory;
-
-    FilenameFilter filter;
-
-    BinaryReader reader;
-
-    Map<String, ChromosomeFileGroup> fileGroups = new HashMap();
-
-    Set<String> dataHeadings = new LinkedHashSet();
-
-    Set<String> chromosomes = new HashSet();
-
-    TrackProperties trackProperties = new TrackProperties();
-
-    public SnpBinaryDataset(
-            String name,
-            TrackType type,
-            String genome,
-            String sourceDirectory,
-            FilenameFilter filter,
-            boolean isSolexa,
-            boolean intLocations) {
-        this.name = name;
-        this.type = type;
-        this.genome = genome;
-        this.sourceDirectory = sourceDirectory;
-        this.filter = filter;
-        this.intLocations = intLocations;
-        this.solexaFormat = isSolexa;
-        reader = new BinaryReader();
-        reader.setLittleEndian(isSolexa);
-        initialize();
-        validate();
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public TrackType getType() {
-        return type;
-    }
-
-    public String[] getChromosomes() {
-        String[] chrs = new String[chromosomes.size()];
-        int i = 0;
-
-        for (String chr : chromosomes) {
-            chrs[i] = chr;
-            i++;
-        }
-
-        return chrs;
-    }
-
-    // TODO - return Snp ids
-    public String[] getFeatureNames(String chr) {
-        return null;
-    }
-
-    /**
-     * Return the dataCache headings (e.g. sample names) for this dataset.
-     *
-     * @return dataCache headings
-     */
-    public String[] getDataHeadings() {
-        String[] headings = new String[dataHeadings.size()];
-        int i = 0;
-
-        for (String h : dataHeadings) {
-            headings[i] = h;
-            i++;
-        }
-
-        return headings;
-    }
-
-    /**
-     * Scan the dataCache directory and create ChromosomeFileGroup objects for each
-     * chromosome. These objects are later used to load the appropriate dataCache for
-     * a chromosome.
-     */
-    private void initialize() {
-        List<File> files = reader.getDataFiles(sourceDirectory, filter);
-
-        for (File file : files) {
-            String[] tokens = file.getName().split("\\.");
-
-            // Get the chromosome identifier. String the "chr" prefix (first 3
-            // characters).  
-            String chr = tokens[0];
-
-            chromosomes.add(chr);
-
-            ChromosomeFileGroup fg = getChromosomeDataFileGroup(chr);
-            String tmp = tokens[1];
-
-            if (tmp.equals("snpid")) {
-                fg.setMarkerIdFile(file);
-            } else if (tmp.equals("snplocation") || tmp.equals("snplocations")) {
-                fg.setLocationFile(file);
-            } else {
-                String arrayName = tmp;
-                int i = 2;
-
-                while (!(tokens[i].equals("bin") || tokens[i].equals("cn"))) {
-                    arrayName += "." + tokens[i];
-                    i++;
-                }
-
-                fg.addDataFile(arrayName, file);
-                dataHeadings.add(arrayName);
-            }
-        }
-
-    }
-
-    private void validate() {
-        boolean valid = true;
-        for (ChromosomeFileGroup fg : fileGroups.values()) {
-            valid = fg.validate() && valid;
-        }
-        if (!valid) {
-            System.err.println("Validation failed.  Exiting");
-            System.exit(-1);
-        }
-    }
-
-    /**
-     * Return a a ChromosomeFileGroup object for the specified chromosome.
-     * Create one if it does not alread exist.
-     *
-     * @param chr
-     * @return
-     */
-    private ChromosomeFileGroup getChromosomeDataFileGroup(String chr) {
-        ChromosomeFileGroup fg = fileGroups.get(chr);
-
-        if (fg == null) {
-            ChromosomeFileGroup.LocationType locType = intLocations ? ChromosomeFileGroup.LocationType.INT : ChromosomeFileGroup.LocationType.LONG;
-            fg = new ChromosomeFileGroup(locType);
-            fileGroups.put(chr, fg);
-        }
-
-        return fg;
-    }
-
-    public String getGenome() {
-        return genome;
-    }
-
-    public int[] getStartLocations(String chr) {
-        int[] start = locations.get(chr);
-
-        if (start == null) {
-
-            // TODO -- handle chip format
-            ChromosomeFileGroup fg = this.getChromosomeDataFileGroup(chr);
-            File locationFile = fg.getLocationFile();
-
-            if (intLocations) {
-                start = reader.readInts(locationFile);
-            } else {
-                long[] longs = reader.readLongs(locationFile);
-
-                start = new int[longs.length];
-
-                int count = 0;
-                for (int i = 0; i < longs.length; i++) {
-                    if (solexaFormat == false || longs[i] >= 0) {
-                        start[count] = (int) longs[i];
-                        count++;
-                    }
-                }
-                //If the count is less than the array size reduce
-                if (count != start.length) {
-                    int[] tmp = new int[count];
-                    System.arraycopy(start, 0, tmp, 0, count);
-                    start = tmp;
-                }
-
-            }
-
-            locations.put(chr, start);
-        }
-        return start;
-    }
-
-    public int[] getEndLocations(String chr) {
-        return null;
-    }
-
-    public float[] getData(String heading, String chr) {
-        ChromosomeFileGroup fg = this.getChromosomeDataFileGroup(chr);
-        File dataFile = fg.getDataFile(heading);
-        if (dataFile == null) {
-            System.out.println("No data file for sample: " + heading +
-                    "  chromosome: " + chr);
-            return null;
-        } else {
-            float[] data = reader.readFloats(dataFile);
-            if (solexaFormat == false) {
-                return data;
-            } else {
-                // Get the start length
-                int nonZeroLength = getStartLocations(chr).length;
-                float[] nonZeroData = new float[nonZeroLength];
-                int count = 0;
-                for (int i = 0; i < data.length; i++) {
-                    if (data[i] > 0) {
-                        nonZeroData[count] = data[i];
-                    }
-
-                }
-                if (count != nonZeroData.length) {
-                    System.err.println("Unexpected data length.  Heading = " + heading + " Chr = " + chr +
-                            " expected: " + nonZeroLength + " actual: " + count);
-                }
-                return nonZeroData;
-            }
-
-        }
-    }
-
-    public int getWindowSpan() {
-        return wSpan;
-    }
-
-    public void setWindowSpan(int wSpan) {
-        this.wSpan = wSpan;
-    }
-
-    public boolean isLogNormalized() {
-        return normalized;
-    }
-
-    public void setNormalized(boolean normalized) {
-        this.normalized = normalized;
-    }
-
-    public boolean isLogValues() {
-        // todo - implement
-        return false;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public float getDataMin() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    public float getDataMax() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    public TrackProperties getTrackProperties() {
-        return trackProperties;
-    }
-}
diff --git a/src/org/broad/igv/preprocess/old/SnpProcessor.java b/src/org/broad/igv/preprocess/old/SnpProcessor.java
deleted file mode 100644
index b1bafad..0000000
--- a/src/org/broad/igv/preprocess/old/SnpProcessor.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.preprocess.old;
-
-import org.broad.igv.data.ProcessingUtils;
-import org.broad.igv.track.TrackType;
-
-import java.io.File;
-import java.io.FilenameFilter;
-import java.util.LinkedHashMap;
-
-/**
- * @author jrobinso
- */
-public class SnpProcessor {
-
-    /**
-     * Example:
-     * NonOverlappingProcessor -dataType solexa  -inputDir C:\projects\gp-modules\modules\SnpViewer\data\solexa_2 -outputFile solexa.h5 -genome mm8
-     *
-     * @param args
-     * @throws java.lang.Exception
-     */
-    public static void main(String args[]) throws Exception {
-
-
-        LinkedHashMap<String, String> argsMap = ProcessingUtils.parseArgs(args);
-        String name = argsMap.get("-name");
-        String inputDirectory = argsMap.get("-inputDir");
-        String outputFile = argsMap.get("-outputFile");
-        String genome = argsMap.get("-genome");
-        String span = argsMap.get("-windowSpan");
-        String normalized = argsMap.get("-normalized");
-        String maxZoomLevel = argsMap.get("-maxZoomLevel");
-
-        // Format of location files
-        boolean intLocations = false;
-        String locationFormat = argsMap.get("-locationFormat");
-        if (locationFormat != null) {
-            intLocations = (locationFormat.equalsIgnoreCase("int"));
-        }
-
-
-        // Data format  (Solexa or not
-        String dataFormat = argsMap.get("-dataFormat");
-        boolean solexaFormat = (dataFormat != null && dataFormat.toUpperCase().equals("SOLEXA"));
-
-
-        int maxZoom = 7;
-        if (maxZoomLevel != null && maxZoomLevel.length() > 0) {
-            try {
-                maxZoom = Integer.parseInt(maxZoomLevel);
-            } catch (NumberFormatException numberFormatException) {
-                maxZoom = 7;
-            }
-        }
-        final String filterString = argsMap.get("-filter");
-
-        String typeString = argsMap.get("-type");
-
-        TrackType type = TrackType.COPY_NUMBER;
-        if (typeString != null) {
-            try {
-                type = TrackType.valueOf(typeString);
-
-            } catch (Exception exception) {
-            }
-        }
-
-
-        FilenameFilter filter = new FilenameFilter() {
-
-            public boolean accept(File dir, String name) {
-                return name.endsWith(".bin") &&
-                        (!name.contains("_random")) &&
-                        (filterString == null ||
-                                name.contains(filterString));
-            }
-        };
-
-
-        int wSpan = 1;
-        if (span != null) {
-            try {
-                wSpan = Integer.parseInt(span);
-            } catch (NumberFormatException numberFormatException) {
-                System.out.println("span must be a number (" + span + ")");
-                System.exit(-1);
-            }
-        }
-
-        // Solex has a span of 25, unless entered otherwise.  1 is the default
-        // and probably means the user neglected to change this in GP
-        if (solexaFormat && wSpan == 1) {
-            wSpan = 25;
-        }
-
-        int norm = 0;
-        if (normalized != null) {
-            try {
-                norm = Integer.parseInt(normalized);
-                System.out.println("normalized = " + norm);
-            } catch (NumberFormatException numberFormatException) {
-                System.out.println("normalized must be a number (" + norm + ")");
-                System.exit(-1);
-            }
-        }
-
-        boolean chipFormat = typeString.equalsIgnoreCase("solexa");
-
-        SnpBinaryDataset ds = new SnpBinaryDataset(name, type, genome,
-                inputDirectory, filter, chipFormat, intLocations);
-        ds.setWindowSpan(wSpan);
-        ds.setNormalized(norm != 0);
-
-        System.out.println("Dataset loaded");
-        System.out.flush();
-
-        NonOverlappingProcessor pr = new NonOverlappingProcessor(ds);
-        pr.setZoomMax(maxZoom);
-        pr.process(outputFile);
-
-    }
-}
diff --git a/src/org/broad/igv/preprocess/old/SolexaBinaryBatchRunner.java b/src/org/broad/igv/preprocess/old/SolexaBinaryBatchRunner.java
deleted file mode 100644
index a252a95..0000000
--- a/src/org/broad/igv/preprocess/old/SolexaBinaryBatchRunner.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.preprocess.old;
-
-import org.broad.igv.data.ProcessingUtils;
-
-import java.io.File;
-import java.io.FilenameFilter;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * @author jrobinso
- */
-public class SolexaBinaryBatchRunner {
-
-    public static void main(String[] args) {
-
-        Map<String, String> argMap = ProcessingUtils.parseArgs(args);
-
-        File sourceDirectory = new File(argMap.get("-inputFile"));
-
-        String filterString = argMap.get("-filter");
-
-        Set<String> result = getSampleNames(sourceDirectory, filterString);
-        System.out.println("# sample = " + result.size());
-
-        File outputFile = new File(argMap.get("-outputFile"));
-        File outputDir = (outputFile.isDirectory() ? outputFile : outputFile.getParentFile());
-
-        for (String sampleName : result) {
-            try {
-                String[] tmp = replaceArg(args, "-filter", sampleName);
-
-                outputFile = new File(outputDir, sampleName + ".h5");
-                replaceArg(args, "-outputFile", outputFile.getCanonicalPath());
-
-                IGVPreprocessor.main(tmp);
-            } catch (Exception ex) {
-                ex.printStackTrace();
-            }
-        }
-    }
-
-    public static Set<String> getSampleNames(File sourceDirectory, final String filterString) {
-
-        FilenameFilter filter = new FilenameFilter() {
-
-            public boolean accept(File dir, String name) {
-                return name.endsWith(".bin") &&
-                        (!name.contains("_random") &&
-                                (filterString == null || name.contains(filterString)));
-            }
-        };
-        File[] files = sourceDirectory.listFiles(filter);
-
-        LinkedHashSet<String> sampleNames = new LinkedHashSet();
-        for (File file : files) {
-            String[] tokens = file.getName().split("\\.");
-
-            //String tmp = tokens[1];
-            String name = tokens[1];
-            int i = 2;
-            while (!(tokens[i].equals("bin") || tokens[i].equals("cn"))) {
-                name += "." + tokens[i];
-                i++;
-            }
-            sampleNames.add(name);
-        }
-        return sampleNames;
-    }
-
-    private static String[] replaceArg(String[] args, String key, String newValue) {
-
-        for (int i = 0; i < args.length - 1; i++) {
-            if (args[i].equals(key)) {
-                args[i + 1] = newValue;
-                return args;
-            }
-        }
-        //No previous value to replace.  Tack on the end
-        String[] newArgs = new String[args.length + 2];
-        System.arraycopy(args, 0, newArgs, 0, args.length);
-        newArgs[newArgs.length - 2] = key;
-        newArgs[newArgs.length - 1] = newValue;
-        return newArgs;
-
-    }
-}
diff --git a/src/org/broad/igv/preprocess/old/SolexaBinaryDataset.java b/src/org/broad/igv/preprocess/old/SolexaBinaryDataset.java
deleted file mode 100644
index 732b613..0000000
--- a/src/org/broad/igv/preprocess/old/SolexaBinaryDataset.java
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * Dataset.java
- *
- * Created on October 29, 2007, 11:29 PM
- *
- * To change this template, choose Tools | Template Manager
- * and open the template in the editor.
- */
-package org.broad.igv.preprocess.old;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import org.broad.igv.data.Dataset;
-import org.broad.igv.feature.Genome;
-import org.broad.igv.feature.GenomeManager;
-import org.broad.igv.track.TrackProperties;
-import org.broad.igv.track.TrackType;
-import org.broad.igv.util.ObjectCache;
-
-import java.io.File;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.util.*;
-
-/**
- * (new SnpBinaryProcessor(name, ds, genomeId, chipFormat, wSpan, norm)).process(outputFile);
- *
- * @author jrobinso
- */
-public class SolexaBinaryDataset implements Dataset {
-
-    private String name;
-
-    private TrackType type = TrackType.CHIP;
-
-    private Genome genome;
-
-    private int wSpan = 25;
-
-    private boolean normalized = false;
-
-    /**
-     * Temp variable to cache dataCache array for a single chromosome.
-     */
-
-    // ObjectCache<String, float[]> dataCache;
-    ObjectCache<String, int[]> locations = new ObjectCache();
-
-    String sourceDirectory;
-
-    FilenameFilter filter;
-
-    BinaryReader reader;
-
-    Map<String, ChromosomeFileGroup> fileGroups = new HashMap();
-
-    Set<String> dataHeadings = new LinkedHashSet();
-
-    Set<String> chromosomes = new HashSet();
-
-    TrackProperties trackProperties = new TrackProperties();
-
-    public SolexaBinaryDataset(
-            String name,
-            TrackType type,
-            String genomeId,
-            String sourceDirectory,
-            FilenameFilter filter) {
-        this.name = name;
-        this.type = type;
-        try {
-            GenomeManager.getInstance().findGenomeAndLoad(genomeId);
-        } catch (IOException e) {
-            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
-        }
-        this.genome = GenomeManager.getInstance().getGenome(genomeId);
-        if (this.genome == null) {
-            throw new RuntimeException("Unknown genome: " + genomeId);
-        }
-        this.sourceDirectory = sourceDirectory;
-        this.filter = filter;
-        reader = new BinaryReader();
-        reader.setLittleEndian(true);
-        initialize();
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public TrackType getType() {
-        return type;
-    }
-
-    public String[] getChromosomes() {
-        String[] chrs = new String[chromosomes.size()];
-        int i = 0;
-
-        for (String chr : chromosomes) {
-            chrs[i] = chr;
-            i++;
-        }
-
-        return chrs;
-    }
-
-    // TODO - return Snp ids
-    public String[] getFeatureNames(String chr) {
-        return null;
-    }
-
-    /**
-     * Return the dataCache headings (e.g. sample names) for this dataset.
-     *
-     * @return dataCache headings
-     */
-    public String[] getDataHeadings() {
-        String[] headings = new String[dataHeadings.size()];
-        int i = 0;
-
-        for (String h : dataHeadings) {
-            headings[i] = h;
-            i++;
-        }
-
-        return headings;
-    }
-
-    // heading+chr -> file
-    Map<String, File> dataFileMap = new HashMap();
-
-    // chr -> # data points
-    Map<String, Integer> chrDataSize = new HashMap();
-
-    /**
-     * Scan the dataCache directory and create ChromosomeFileGroup objects for each
-     * chromosome. These objects are later used to load the appropriate dataCache for
-     * a chromosome.
-     */
-    private void initialize() {
-        List<File> files = reader.getDataFiles(sourceDirectory, filter);
-
-        for (File file : files) {
-            String[] tokens = file.getName().split("\\.");
-
-            // Get the chromosome identifier. String the "chr" prefix (first 3
-            // characters).  
-            String chr = tokens[0];
-
-            chromosomes.add(chr);
-
-            //String tmp = tokens[1];
-            String arrayName = tokens[1];
-            int i = 2;
-            while (!(tokens[i].equals("bin") || tokens[i].equals("cn"))) {
-                arrayName += "." + tokens[i];
-                i++;
-            }
-
-            String key = arrayName + chr;
-            dataFileMap.put(key, file);
-
-
-            int nFilePts = (int) (file.length() / 4);
-
-            //if(!chrDataSize.containsKey(chr)) {
-            //    int nPts = genome.getChromosome(chr).getLength() / 25;
-            //    chrDataSize.put(chr, nPts);
-            //}
-            Integer nDataPoints = chrDataSize.get(chr);
-            if (nDataPoints == null) {
-                chrDataSize.put(chr, nFilePts);
-            } else {
-                chrDataSize.put(chr, Math.max(nFilePts, nDataPoints));
-            }
-
-            dataHeadings.add(arrayName);
-        }
-
-        for (Map.Entry<String, Integer> tmp : chrDataSize.entrySet()) {
-            System.out.println("Chr: " + tmp.getKey() + "  Size= " + tmp.getValue());
-        }
-
-
-    }
-
-    public String getGenome() {
-        return genome.getId();
-    }
-
-    public int[] getStartLocations(String chr) {
-        int[] start = locations.get(chr);
-
-        if (start == null) {
-
-            int nPts = chrDataSize.get(chr);
-            start = new int[nPts];
-            for (int i = 0; i < nPts; i++) {
-                start[i] = 25 * i;
-            }
-            locations.put(chr, start);
-        }
-        return start;
-    }
-
-    public int[] getEndLocations(String chr) {
-        return null;
-    }
-
-    public float[] getData(String heading, String chr) {
-        String key = heading + chr;
-        File dataFile = this.dataFileMap.get(key);
-        if (dataFile == null) {
-            System.out.println("No data file for sample: " + heading +
-                    "  chromosome: " + chr);
-            return null;
-        } else {
-            int nPts = this.chrDataSize.get(chr);
-            int nFilePts = (int) (dataFile.length() / 4);
-            float[] fileData = reader.readFloats(dataFile);
-
-            float[] data = new float[nPts];
-            int i = 0;
-            while (i < nFilePts) {
-                float fd = fileData[i];
-                data[i] = (fd < 0 ? Float.NaN : fd);
-                i++;
-            }
-            while (i < data.length) {
-                data[i] = Float.NaN;
-                i++;
-            }
-
-
-            return data;
-
-        }
-    }
-
-    public int getWindowSpan() {
-        return wSpan;
-    }
-
-    public void setWindowSpan(int wSpan) {
-        this.wSpan = wSpan;
-    }
-
-    public boolean isLogNormalized() {
-        return normalized;
-    }
-
-    public void setNormalized(boolean normalized) {
-        this.normalized = normalized;
-    }
-
-    public boolean isLogValues() {
-        // todo - implement
-        return false;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public float getDataMin() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    public float getDataMax() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    public TrackProperties getTrackProperties() {
-        return trackProperties;
-    }
-
-}
diff --git a/src/org/broad/igv/preprocess/old/TestUtils.java b/src/org/broad/igv/preprocess/old/TestUtils.java
deleted file mode 100644
index d7505ef..0000000
--- a/src/org/broad/igv/preprocess/old/TestUtils.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.preprocess.old;
-
-import org.broad.igv.feature.BasicFeature;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Create a test dataset
- *
- * @author jrobinso
- */
-public class TestUtils {
-
-    public static void main(String[] args) {
-        System.out.println("go");
-        int nPts = 247249719;
-        int[] data = new int[nPts];
-        //int [] data2 = new int[data.length];
-        //for(int i=0; i<data2.length; i++) {
-        //    data2[i]=data[i];
-        //}
-
-        System.gc();
-        long usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
-        System.out.println("Memory = " + (usedMemory / 1000000) + " MB");
-        System.out.println("Memory / object = " + (usedMemory / data.length));
-        System.out.println("# objects = " + data.length);
-
-    }
-
-    protected static void createFeatureObjects() {
-        System.out.println("go");
-        List<BasicFeature> features = new ArrayList();
-        for (double n = 0; n < 247249719; n++) {
-
-            features.add(new BasicFeature("chr1", (int) n, (int) n));
-        }
-
-        System.gc();
-        long usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
-        System.out.println("Memory = " + usedMemory / 1000000);
-        System.out.println("Memory / object = " + (usedMemory / features.size()));
-        System.out.println("# objects = " + features.size());
-    }
-}
diff --git a/src/org/broad/igv/preprocess/resources/hg16.sizes b/src/org/broad/igv/preprocess/resources/hg16.sizes
deleted file mode 100644
index 72b9c64..0000000
--- a/src/org/broad/igv/preprocess/resources/hg16.sizes
+++ /dev/null
@@ -1,42 +0,0 @@
-chr1 246127941
-chr2 243615958
-chr3 199344050
-chr4 191731959
-chr5 181034922
-chr6 170914576
-chr7 158545518
-chr8 146308819
-chr9 136372045
-chr10 135037215
-chr11 134482954
-chr12 132078379
-chr13 113042980
-chr14 105311216
-chr15 100256656
-chr16 90041932
-chr17 81860266
-chr18 76115139
-chr19 63811651
-chr20 63741868
-chr21 46976097
-chr22 49396972
-chrX 153692391
-chrY 50286555
-chrM 16571
-chr1_random 6515988
-chr2_random 1104831
-chr3_random 749256
-chr4_random 648024
-chr5_random 143687
-chr6_random 2055751
-chr7_random 632637
-chr8_random 1499381
-chr9_random 2766341
-chr10_random 1043775
-chr13_random 189598
-chr15_random 1132826
-chr17_random 2549222
-chr18_random 4262
-chr19_random 92689
-chrX_random 3403558
-chrUn_random 3349625
diff --git a/src/org/broad/igv/preprocess/resources/hg17.sizes b/src/org/broad/igv/preprocess/resources/hg17.sizes
deleted file mode 100644
index ecb2f23..0000000
--- a/src/org/broad/igv/preprocess/resources/hg17.sizes
+++ /dev/null
@@ -1,44 +0,0 @@
-chr1 245522847
-chr2 243018229
-chr3 199505740
-chr4 191411218
-chr5 180857866
-chr6 170975699
-chr7 158628139
-chr8 146274826
-chr9 138429268
-chr10 135413628
-chr11 134452384
-chr12 132449811
-chr13 114142980
-chr14 106368585
-chr15 100338915
-chr16 88827254
-chr17 78774742
-chr18 76117153
-chr19 63811651
-chr20 62435964
-chr21 46944323
-chr22 49554710
-chrX 154824264
-chrY 57701691
-chrM 16571
-chr1_random 3897131
-chr2_random 418158
-chr3_random 970716
-chr4_random 1030282
-chr5_random 143687
-chr6_random 1875562
-chr7_random 778964
-chr8_random 943810
-chr9_random 1312665
-chr10_random 113275
-chr12_random 466818
-chr13_random 186858
-chr15_random 784346
-chr16_random 105485
-chr17_random 2618010
-chr18_random 4262
-chr19_random 301858
-chr22_random 257318
-chrX_random 1719168
diff --git a/src/org/broad/igv/preprocess/resources/hg18.sizes b/src/org/broad/igv/preprocess/resources/hg18.sizes
deleted file mode 100644
index c3e4f3d..0000000
--- a/src/org/broad/igv/preprocess/resources/hg18.sizes
+++ /dev/null
@@ -1,45 +0,0 @@
-chr1 247249719
-chr2 242951149
-chr3 199501827
-chr4 191273063
-chr5 180857866
-chr6 170899992
-chr7 158821424
-chr8 146274826
-chr9 140273252
-chr10 135374737
-chr11 134452384
-chr12 132349534
-chr13 114142980
-chr14 106368585
-chr15 100338915
-chr16 88827254
-chr17 78774742
-chr18 76117153
-chr19 63811651
-chr20 62435964
-chr21 46944323
-chr22 49691432
-chrX 154913754
-chrY 57772954
-chrM 16571
-chr1_random 1663265
-chr2_random 185571
-chr3_random 749256
-chr4_random 842648
-chr5_random 143687
-chr6_random 1875562
-chr7_random 549659
-chr8_random 943810
-chr9_random 1146434
-chr10_random 113275
-chr11_random 215294
-chr13_random 186858
-chr15_random 784346
-chr16_random 105485
-chr17_random 2617613
-chr18_random 4262
-chr19_random 301858
-chr21_random 1679693
-chr22_random 257318
-chrX_random 1719168
diff --git a/src/org/broad/igv/preprocess/resources/mm8.sizes b/src/org/broad/igv/preprocess/resources/mm8.sizes
deleted file mode 100644
index d24ae05..0000000
--- a/src/org/broad/igv/preprocess/resources/mm8.sizes
+++ /dev/null
@@ -1,34 +0,0 @@
-chr1	197069962
-chr2	181976762
-chr3	159872112
-chr4	155029701
-chr5	152003063
-chr6	149525685
-chr7	145134094
-chr8	132085098
-chr10	129959148
-chr9	124000669
-chr14	123978870
-chr11	121798632
-chr13	120614378
-chr12	120463159
-chr15	103492577
-chr16	98252459
-chr17	95177420
-chr18	90736837
-chr19	61321190
-chrX	165556469
-chrY	16029404
-chrM	16299
-chr10_random	10781
-chr13_random	436191
-chr15_random	105932
-chr17_random	89091
-chr1_random	172274
-chr5_random	2921247
-chr7_random	243910
-chr8_random	206961
-chr9_random	17232
-chrUn_random	1540053
-chrX_random	39696
-chrY_random	14577732
diff --git a/src/org/broad/igv/preprocess/resources/mm9.sizes b/src/org/broad/igv/preprocess/resources/mm9.sizes
deleted file mode 100644
index 68be990..0000000
--- a/src/org/broad/igv/preprocess/resources/mm9.sizes
+++ /dev/null
@@ -1,33 +0,0 @@
-chr1	197195432
-chr2	181748087
-chr3	159599783
-chr4	155630120
-chr5	152537259
-chr7	152524553
-chr6	149517037
-chr8	131738871
-chr10	129993255
-chr14	125194864
-chr9	124076172
-chr11	121843856
-chr12	121257530
-chr13	120284312
-chr15	103494974
-chr16	98319150
-chr17	95272651
-chr18	90772031
-chr19	61342430
-chrX	166650296
-chrY	58682461
-chr1_random	1231697
-chr3_random	41899
-chr4_random	160594
-chr5_random	357350
-chr7_random	362490
-chr8_random	849593
-chr9_random	449403
-chr13_random	400311
-chr16_random	3994
-chr17_random	628739
-chrX_random	1785075
-chrY_random	58682461
diff --git a/src/org/broad/igv/remote/HTTPConnectionTests.java b/src/org/broad/igv/remote/HTTPConnectionTests.java
index b68de4b..c204fc5 100644
--- a/src/org/broad/igv/remote/HTTPConnectionTests.java
+++ b/src/org/broad/igv/remote/HTTPConnectionTests.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,6 +22,8 @@
  */
 package org.broad.igv.remote;
 
+import org.broad.igv.util.IGVHttpUtils;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.HttpURLConnection;
@@ -43,7 +45,7 @@ public class HTTPConnectionTests {
 
         //URL url = new URL("http://www.broadinstitute.org/~jrobinso/test.tdf");
         URL url = new URL("http://www.broadinstitute.org/igv/resources/dataServerRegistry.txt");
-        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+        HttpURLConnection connection = (HttpURLConnection) IGVHttpUtils.openConnection(url);
 
         connection.setRequestMethod("HEAD");
 
@@ -60,7 +62,7 @@ public class HTTPConnectionTests {
 
 
         URL url = new URL("http://www.broadinstitute.org/igv/resources/dataServerRegistry.txt");
-        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+        HttpURLConnection connection = (HttpURLConnection) IGVHttpUtils.openConnection(url);
 
         connection.setRequestMethod("HEAD");
 
@@ -86,7 +88,7 @@ public class HTTPConnectionTests {
 
         URL url = new URL("http://www.broadinstitute.org/igv/resources/dataServerRegistry.txt");
         //URL url = new URL("http://www.broadinstitute.org/~jrobinso/dataServerRegistry.txt");
-        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+        HttpURLConnection connection = (HttpURLConnection) IGVHttpUtils.openConnection(url);
         connection.setRequestMethod("POST");
 
         String byteRange = "bytes=" + (len - 10) + "-" + (len - 1);
diff --git a/src/org/broad/igv/remote/SequenceServletWrapper.java b/src/org/broad/igv/remote/SequenceServletWrapper.java
index 6a2893b..099e2a8 100644
--- a/src/org/broad/igv/remote/SequenceServletWrapper.java
+++ b/src/org/broad/igv/remote/SequenceServletWrapper.java
@@ -27,7 +27,7 @@ import org.apache.log4j.Logger;
 import org.broad.igv.feature.GenomeDescriptor;
 import org.broad.igv.feature.GenomeManager;
 import org.broad.igv.ui.util.MessageUtils;
-import org.broad.igv.util.HttpUtils;
+import org.broad.igv.util.IGVHttpUtils;
 
 import java.awt.*;
 import java.io.*;
@@ -47,7 +47,7 @@ public class SequenceServletWrapper {
     private static Logger logger = Logger.getLogger(SequenceServletWrapper.class);
 
 
-    public static byte[] readBytes(String sequenceURL, String chr, int start, int end) {
+    public static byte[] readBytes(String location, String chr, int start, int end) {
 
         byte[] bytes = new byte[end - start];
 
@@ -56,10 +56,12 @@ public class SequenceServletWrapper {
         }
 
 
+
+
         try {
 
-            URL url = new URL(sequenceURL);
-            URLConnection connection = url.openConnection();
+            URL url = new URL(location);
+            URLConnection connection = IGVHttpUtils.openConnection(url);
             connection.setConnectTimeout(CONNECTION_TIMEOUT);
             connection.setDoOutput(true);
 
@@ -92,7 +94,7 @@ public class SequenceServletWrapper {
 
                 showUnavailableMessage();
 
-                logger.error("Error retrieving sequence from : " + sequenceURL + ex.getMessage());
+                logger.error("Error retrieving sequence from : " + location + ex.getMessage());
             }
             return null;
         }
diff --git a/src/org/broad/igv/renderer/AbstractColorScale.java b/src/org/broad/igv/renderer/AbstractColorScale.java
index f88ee14..a1a2947 100644
--- a/src/org/broad/igv/renderer/AbstractColorScale.java
+++ b/src/org/broad/igv/renderer/AbstractColorScale.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/renderer/BarChartRenderer.java b/src/org/broad/igv/renderer/BarChartRenderer.java
index 4c35883..89e0e71 100644
--- a/src/org/broad/igv/renderer/BarChartRenderer.java
+++ b/src/org/broad/igv/renderer/BarChartRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/renderer/BasicFeatureRenderer.java b/src/org/broad/igv/renderer/BasicFeatureRenderer.java
index 3695bf3..9486ea5 100644
--- a/src/org/broad/igv/renderer/BasicFeatureRenderer.java
+++ b/src/org/broad/igv/renderer/BasicFeatureRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -19,15 +19,13 @@ package org.broad.igv.renderer;
 
 //~--- non-JDK imports --------------------------------------------------------
 
-import cern.colt.map.OpenIntObjectHashMap;
 import org.apache.log4j.Logger;
-import org.broad.igv.PreferenceManager;
 import org.broad.igv.feature.*;
-import org.broad.igv.feature.dranger.DRangerFeature;
+import org.broad.igv.track.FeatureTrack;
 import org.broad.igv.track.RenderContext;
-import org.broad.igv.track.Track;
 import org.broad.igv.track.TrackType;
 import org.broad.igv.ui.FontManager;
+import org.broad.igv.util.ColorUtilities;
 
 import java.awt.*;
 import java.awt.font.LineMetrics;
@@ -54,6 +52,11 @@ public class BasicFeatureRenderer extends FeatureRenderer {
     private Font font;
     private boolean drawBoundary = false;
 
+
+    float viewLimitMin = Float.NaN;
+    float viewLimitMax = Float.NaN;
+
+
     // Use the max of these values to determine where
     // text should be drawn
     private double lastFeatureLineMaxY = 0;
@@ -62,12 +65,11 @@ public class BasicFeatureRenderer extends FeatureRenderer {
 
     // Constants
     static protected final int NORMAL_STRAND_Y_OFFSET = 14;
-    static protected final int POSITIVE_STRAND_Y_OFFSET = 5;
-    static protected final int NEGATIVE_STRAND_Y_OFFSET = 15;
     static protected final int ARROW_SPACING = 30;
     static protected final int NO_STRAND_THICKNESS = 2;
     static protected final int REGION_STRAND_THICKNESS = 4;
     final int BLOCK_HEIGHT = 14;
+    final int THIN_BLOCK_HEIGHT = 6;
 
     /**
      * Note:  assumption is that featureList is sorted by pStart position.
@@ -77,8 +79,10 @@ public class BasicFeatureRenderer extends FeatureRenderer {
      * @param trackRectangle
      * @param track
      */
-    public void renderFeatures(List<Feature> featureList, RenderContext context,
-                               Rectangle trackRectangle, Track track) {
+    public void renderFeatures(List<Feature> featureList,
+                               RenderContext context,
+                               Rectangle trackRectangle,
+                               FeatureTrack track) {
 
         double origin = context.getOrigin();
         double locScale = context.getScale();
@@ -94,7 +98,7 @@ public class BasicFeatureRenderer extends FeatureRenderer {
             // Create a graphics object to draw font names.  Graphics are not cached
             // by font, only by color, so its neccessary to create a new one to prevent
             // affecting other tracks.
-            font = FontManager.getScalableFont(9);
+            font = FontManager.getScalableFont(track.getFontSize());
             Graphics2D fontGraphics = (Graphics2D) context.getGraphic2DForColor(Color.BLACK).create();
             fontGraphics.setFont(font);
 
@@ -110,6 +114,10 @@ public class BasicFeatureRenderer extends FeatureRenderer {
 
 
             int lastFeatureEndedAtPixelX = -9999;
+            int lastPixelEnd = -1;
+            int occludedCount = 0;
+            int maxOcclusions = 2;
+
             Feature featureArray[] = featureList.toArray(new Feature[featureList.size()]);
             for (int i = 0; i < featureArray.length; i++) {
 
@@ -131,8 +139,21 @@ public class BasicFeatureRenderer extends FeatureRenderer {
                 if ((virtualPixelEnd >= trackRectangleX) && (virtualPixelStart <= trackRectangleMaxX)) {
 
                     //
-                    int pixelEnd = (int) virtualPixelEnd;
-                    int pixelStart = (int) virtualPixelStart;
+                    int pixelEnd = (int) Math.min(trackRectangleMaxX, virtualPixelEnd);
+                    int pixelStart = (int) Math.max(trackRectangleX, virtualPixelStart);
+
+
+                    if (pixelEnd <= lastPixelEnd) {
+                        if (occludedCount >= maxOcclusions) {
+                            continue;
+                        } else {
+                            occludedCount++;
+                        }
+                    } else {
+                        occludedCount = 0;
+                        lastPixelEnd = pixelEnd;
+                    }
+
                     Color color = getFeatureColor(feature, track);
 
 
@@ -141,8 +162,16 @@ public class BasicFeatureRenderer extends FeatureRenderer {
                     // Get the feature's direction
                     Strand strand = feature.getStrand();
 
-                    // Draw lines
-                    drawFeatureBlock(pixelStart, pixelEnd, trackRectangle, strand, hasRegions, g2D);
+                    // Draw block
+                    int pixelThickStart = pixelStart;
+                    int pixelThickEnd = pixelEnd;
+                    if (feature instanceof BasicFeature) {
+                        BasicFeature bf = (BasicFeature) feature;
+                        pixelThickStart = (int) Math.max(trackRectangleX, (bf.getThickStart() - origin) / locScale);
+                        pixelThickEnd = (int) Math.min(trackRectangleMaxX, (bf.getThickEnd() - origin) / locScale);
+                    }
+
+                    drawFeatureBlock(pixelStart, pixelEnd, pixelThickStart, pixelThickEnd, trackRectangle, strand, hasRegions, g2D);
 
                     // Determine the y offset of features based on strand type
 
@@ -150,11 +179,11 @@ public class BasicFeatureRenderer extends FeatureRenderer {
                     // feature, or orientation.  If the feature has any exons
                     // at all indicate by filling a small rect
 
-                    int yOffset = trackRectangle.y + NORMAL_STRAND_Y_OFFSET / 2;
+                    int pixelYCenter = trackRectangle.y + NORMAL_STRAND_Y_OFFSET / 2;
 
                     if ((pixelEnd - pixelStart < 3) && hasRegions) {
 
-                        drawVerticalFeatureBounds(pixelStart, pixelEnd, yOffset, g2D);
+                        drawVerticalFeatureBounds(pixelStart, pixelEnd, pixelYCenter, g2D);
                     } else {
 
                         Graphics2D arrowGraphics = hasRegions
@@ -162,12 +191,12 @@ public class BasicFeatureRenderer extends FeatureRenderer {
                                 : context.getGraphic2DForColor(Color.WHITE);
 
                         // Draw the directional arrows
-                        drawStrandArrows(feature, pixelStart, pixelEnd, yOffset, arrowGraphics);
+                        drawStrandArrows(feature, pixelStart, pixelEnd, pixelYCenter, arrowGraphics);
 
                         if (hasRegions) {
 
                             // Draw the individual block of a feature
-                            drawBlockRegions(feature, yOffset, context, g2D, trackRectangle);
+                            drawExons(feature, pixelYCenter, context, g2D, trackRectangle);
                         }
 
                     }
@@ -198,6 +227,15 @@ public class BasicFeatureRenderer extends FeatureRenderer {
                         }
                     }
 
+                    // If this is the highlight feature highlight it
+                    if (getHighlightFeature() == feature) {
+                        int yStart = pixelYCenter - BLOCK_HEIGHT / 2 - 1;
+                        Graphics2D highlightGraphics = context.getGraphic2DForColor(Color.cyan);
+                        highlightGraphics.drawRect(pixelStart - 1, yStart, (pixelEnd - pixelStart + 2), BLOCK_HEIGHT + 2);
+
+
+                    }
+
                 }
             }
 
@@ -218,7 +256,8 @@ public class BasicFeatureRenderer extends FeatureRenderer {
      * @param g
      * @return The stroke used to draw the line.
      */
-    final private void drawFeatureBlock(int pixelStart, int pixelEnd, Rectangle trackRectangle,
+    final private void drawFeatureBlock(int pixelStart, int pixelEnd, int pixelThickStart, int pixelThickEnd,
+                                        Rectangle trackRectangle,
                                         Strand strand, boolean hasRegions, Graphics2D g) {
 
         Graphics2D g2D = (Graphics2D) g.create();
@@ -226,11 +265,17 @@ public class BasicFeatureRenderer extends FeatureRenderer {
         if (!hasRegions) {
             int yOffset = trackRectangle.y + NORMAL_STRAND_Y_OFFSET / 2;
 
-            // No regions so draw and even thicker line
-            // g2D.fillRect(pStart, yOffset - BLOCK_HEIGHT / 2, width, BLOCK_HEIGHT);
+            if (pixelThickStart > pixelStart) {
+                g2D.fillRect(pixelStart, yOffset - (THIN_BLOCK_HEIGHT) / 2,
+                        Math.max(1, pixelThickStart - pixelStart), (THIN_BLOCK_HEIGHT));
+            }
+            if (pixelThickEnd > 0 && pixelThickEnd < pixelEnd) {
+                g2D.fillRect(pixelThickEnd, yOffset - (THIN_BLOCK_HEIGHT) / 2,
+                        Math.max(1, pixelEnd - pixelThickEnd), (THIN_BLOCK_HEIGHT));
+            }
 
-            g2D.fillRect(pixelStart, yOffset - (BLOCK_HEIGHT - 4) / 2,
-                    Math.max(1, pixelEnd - pixelStart), (BLOCK_HEIGHT - 4));
+            g2D.fillRect(pixelThickStart, yOffset - (BLOCK_HEIGHT - 4) / 2,
+                    Math.max(1, pixelThickEnd - pixelThickStart), (BLOCK_HEIGHT - 4));
             lastFeatureLineMaxY = yOffset + BLOCK_HEIGHT - 4;
         } else {
 
@@ -239,12 +284,9 @@ public class BasicFeatureRenderer extends FeatureRenderer {
             float lineThickness = ((BasicStroke) g.getStroke()).getLineWidth();
             if (strand == null) {
 
-                // The stroke used to draw the line
-                Stroke stroke = g2D.getStroke();
-
                 // Double the line thickness
                 lineThickness *= NO_STRAND_THICKNESS;
-                stroke = new BasicStroke(lineThickness);
+                Stroke stroke = new BasicStroke(lineThickness);
                 g2D.setStroke(stroke);
             }
             g2D.drawLine(pixelStart, yOffset, pixelEnd, yOffset);
@@ -253,6 +295,7 @@ public class BasicFeatureRenderer extends FeatureRenderer {
         g2D.dispose();
     }
 
+
     private void drawVerticalFeatureBounds(int pixelStart, int pixelEnd, int yOffset,
                                            Graphics2D g2D) {
 
@@ -260,15 +303,14 @@ public class BasicFeatureRenderer extends FeatureRenderer {
             int yStart = yOffset - BLOCK_HEIGHT / 2;
             g2D.drawLine(pixelStart, yStart, pixelStart, yStart + BLOCK_HEIGHT);
         } else {
-            g2D.fillRect(pixelStart, yOffset - BLOCK_HEIGHT / 2, pixelEnd - pixelStart,
-                    BLOCK_HEIGHT);
+            g2D.fillRect(pixelStart, yOffset - BLOCK_HEIGHT / 2, pixelEnd - pixelStart, BLOCK_HEIGHT);
         }
 
         lastFeatureBoundsMaxY = yOffset + BLOCK_HEIGHT / 2;
     }
 
-    protected void drawBlockRegions(Feature gene, int yOffset, RenderContext context,
-                                    Graphics2D g2D, Rectangle trackRectangle) {
+    protected void drawExons(Feature gene, int yOffset, RenderContext context,
+                             Graphics2D g2D, Rectangle trackRectangle) {
 
         Graphics exonNumberGraphics = g2D.create();
         exonNumberGraphics.setColor(Color.BLACK);
@@ -283,50 +325,56 @@ public class BasicFeatureRenderer extends FeatureRenderer {
         Graphics2D fontGraphics = context.getGraphic2DForColor(Color.WHITE);
 
         for (Exon exon : gene.getExons()) {
-            int pStart = getPixelFromChromosomeLocation(exon.getChromosome(), exon.getStart(),
-                    theOrigin, locationScale);
-            int pEnd = getPixelFromChromosomeLocation(exon.getChromosome(), exon.getEnd(),
-                    theOrigin, locationScale);
+            int pStart = getPixelFromChromosomeLocation(exon.getChr(), exon.getStart(), theOrigin, locationScale);
+            int pEnd = getPixelFromChromosomeLocation(exon.getChr(), exon.getEnd(), theOrigin, locationScale);
+
 
-            if ((pEnd > 0) && (pStart < trackRectangle.getMaxX())) {
+            if ((pEnd >= trackRectangle.getX()) && (pStart <= trackRectangle.getMaxX())) {
                 int pCdStart =
-                        Math.min(pEnd,
-                                Math.max(pStart,
-                                        getPixelFromChromosomeLocation(exon.getChromosome(),
-                                                exon.getCdStart(), theOrigin, locationScale)));
-                int pCdEnd = Math.max(pStart,
-                        Math.min(pEnd,
-                                getPixelFromChromosomeLocation(exon.getChromosome(),
-                                        exon.getCdEnd(), theOrigin, locationScale)));
-
-
-                if (pCdStart > pStart) {
-                    g2D.fillRect(pStart, yOffset - NON_CODING_HEIGHT / 2, pCdStart - pStart,
-                            NON_CODING_HEIGHT);
-                    pStart = pCdStart;
-                }
-                if (pCdEnd < pEnd) {
-                    g2D.fillRect(pCdEnd, yOffset - NON_CODING_HEIGHT / 2, pEnd - pCdEnd,
-                            NON_CODING_HEIGHT);
-                    pEnd = pCdEnd;
-                }
+                        Math.min(pEnd, Math.max(pStart,
+                                getPixelFromChromosomeLocation(exon.getChr(),
+                                        exon.getCdStart(), theOrigin, locationScale)));
+                int pCdEnd = Math.max(pStart, Math.min(pEnd,
+                        getPixelFromChromosomeLocation(exon.getChr(),
+                                exon.getCdEnd(), theOrigin, locationScale)));
+
+                // Entire exon is UTR
+                if (exon.isUTR()) {
+                    int pClippedStart = (int) Math.max(pStart, trackRectangle.getX());
+                    int pClippedEnd = (int) Math.min(pEnd, trackRectangle.getMaxX());
+                    int pClippedWidth = pClippedEnd - pClippedStart;
+                    g2D.fillRect(pClippedStart, yOffset - NON_CODING_HEIGHT / 2, pClippedWidth, NON_CODING_HEIGHT);
+
+                } else {
+                    // Exon contains 5' UTR -- draw non-coding part
+                    if (pCdStart > pStart) {
+                        int pClippedStart = (int) Math.max(pStart, trackRectangle.getX());
+                        int pClippedEnd = (int) Math.min(pCdStart, trackRectangle.getMaxX());
+                        int pClippedWidth = pClippedEnd - pClippedStart;
+                        g2D.fillRect(pClippedStart, yOffset - NON_CODING_HEIGHT / 2, pClippedWidth, NON_CODING_HEIGHT);
+                        pStart = pCdStart;
+                    }
+                    //  Exon contains 3' UTR  -- draw non-coding part
+                    if (pCdEnd < pEnd) {
+                        int pClippedStart = (int) Math.max(pCdEnd, trackRectangle.getX());
+                        int pClippedEnd = (int) Math.min(pEnd, trackRectangle.getMaxX());
+                        int pClippedWidth = pClippedEnd - pClippedStart;
+                        g2D.fillRect(pClippedStart, yOffset - NON_CODING_HEIGHT / 2, pClippedWidth, NON_CODING_HEIGHT);
+                        pEnd = pCdEnd;
+                    }
 
-                if ((exon.getCdStart() < exon.getEnd()) && (exon.getCdEnd() > exon.getStart())) {
-                    int width = Math.max(2, pEnd - pStart);
-                    g2D.fillRect(pStart, yOffset - BLOCK_HEIGHT / 2, width, BLOCK_HEIGHT);
-                    if (PreferenceManager.getInstance().getDrawExonNumbers() && (width > 4)) {
+                    // At least part of this exon is coding.  Draw the coding part.
+                    if ((exon.getCdStart() < exon.getEnd()) && (exon.getCdEnd() > exon.getStart())) {
+                        int pClippedStart = (int) Math.max(pStart, trackRectangle.getX());
+                        int pClippedEnd = (int) Math.min(pEnd, trackRectangle.getMaxX());
+                        int pClippedWidth = Math.max(2, pClippedEnd - pClippedStart);
+                        g2D.fillRect(pClippedStart, yOffset - BLOCK_HEIGHT / 2, pClippedWidth, BLOCK_HEIGHT);
 
-                        // int exonNumber = i + 1;
-                        // Rectangle numberRect = new Rectangle(pStart, yOffset + BLOCK_HEIGHT / 2,
-                        // pEnd - pStart, 10);
-                        // GraphicUtils.drawCenteredChar(String.valueOf(exonNumber), numberRect, exonNumberGraphics);
                     }
                 }
 
-                Color c = g2D.getColor();
-                g2D.setColor(Color.white);
-                drawStrandArrows(gene, pStart + ARROW_SPACING / 2, pEnd, yOffset, g2D);
-                g2D.setColor(c);
+                Graphics2D arrowGraphics = context.getGraphic2DForColor(Color.white);
+                drawStrandArrows(gene, pStart + ARROW_SPACING / 2, pEnd, yOffset, arrowGraphics);
 
 
                 if (locationScale < 0.25) {
@@ -344,8 +392,7 @@ public class BasicFeatureRenderer extends FeatureRenderer {
 
     }
 
-    protected void drawStrandArrows(Feature feature, int pixelStart, int pixelEnd, int yOffset,
-                                    Graphics2D g2D) {
+    protected void drawStrandArrows(Feature feature, int pixelStart, int pixelEnd, int yOffset, Graphics2D g2D) {
 
         // Don't draw strand arrows for very small regions
         if ((pixelEnd - pixelStart < 6)) {
@@ -397,11 +444,11 @@ public class BasicFeatureRenderer extends FeatureRenderer {
         }
 
         FontMetrics fm = g2D.getFontMetrics();
+        int fontSize = fm.getFont().getSize();
         int nameWidth = fm.stringWidth(name);
-        Rectangle2D stringBounds = fm.getStringBounds(name, g2D);
-        int nameStart = (int) (pixelStart + pixelEnd - nameWidth) / 2;
+        int nameStart = (pixelStart + pixelEnd - nameWidth) / 2;
 
-        if (nameStart > (lastFeatureEndedAtPixelX + 10)) {
+        if (nameStart > (lastFeatureEndedAtPixelX + fontSize)) {
 
             // g2D.clearRect(xString2, textBaselineY, (int) stringBounds.getWidth(), (int) stringBounds.getHeight());
             g2D.drawString(name, nameStart, textBaselineY);
@@ -442,14 +489,13 @@ public class BasicFeatureRenderer extends FeatureRenderer {
                 // TODO -- get base from previous exon and compute aaSequence.  For now skipping drawing
                 // the AA label
                 int cdStart = Math.max(exon.getCdStart(), exon.getStart());
-                aaRect.x = getPixelFromChromosomeLocation(exon.getChromosome(), cdStart, theOrigin,
+                aaRect.x = getPixelFromChromosomeLocation(exon.getChr(), cdStart, theOrigin,
                         locationScale);
-                aaRect.width = getPixelFromChromosomeLocation(exon.getChromosome(),
+                aaRect.width = getPixelFromChromosomeLocation(exon.getChr(),
                         aaSeqStartPosition, theOrigin, locationScale) - aaRect.x;
 
                 if (trackRectangle.intersects(aaRect)) {
-                    Graphics2D bgGraphics = context.getGraphic2DForColor(odd
-                            ? AA_COLOR_1 : AA_COLOR_2);
+                    Graphics2D bgGraphics = context.getGraphic2DForColor(odd ? AA_COLOR_1 : AA_COLOR_2);
                     odd = !odd;
                     bgGraphics.fill(aaRect);
                 }
@@ -459,12 +505,10 @@ public class BasicFeatureRenderer extends FeatureRenderer {
             for (AminoAcid acid : aaSequence.getSequence()) {
                 if (acid != null) {
 
-                    int px = getPixelFromChromosomeLocation(exon.getChromosome(),
-                            aaSeqStartPosition, theOrigin, locationScale);
-                    int px2 = getPixelFromChromosomeLocation(exon.getChromosome(),
-                            aaSeqStartPosition + 3, theOrigin, locationScale);
+                    int px = getPixelFromChromosomeLocation(exon.getChr(), aaSeqStartPosition, theOrigin, locationScale);
+                    int px2 = getPixelFromChromosomeLocation(exon.getChr(), aaSeqStartPosition + 3, theOrigin, locationScale);
 
-                    if ((px < trackRectangle.getMaxX()) && (px2 > 0)) {
+                    if ((px <= trackRectangle.getMaxX()) && (px2 >= trackRectangle.getX())) {
 
                         // {
 
@@ -472,10 +516,11 @@ public class BasicFeatureRenderer extends FeatureRenderer {
                         aaRect.width = px2 - px;
 
 
-                        Graphics2D bgGraphics = context.getGraphic2DForColor(odd
-                                ? AA_COLOR_1 : AA_COLOR_2);
+                        Graphics2D bgGraphics = context.getGraphic2DForColor(odd ? AA_COLOR_1 : AA_COLOR_2);
                         odd = !odd;
-                        if (((acid.getSymbol() == 'M') && (((gene.getStrand() == Strand.POSITIVE) && (aaSeqStartPosition == exon.getCdStart())) || ((gene.getStrand() == Strand.NEGATIVE) && (aaSeqStartPosition == exon.getCdEnd() - 3))))) {
+                        if (((acid.getSymbol() == 'M') && (((gene.getStrand() == Strand.POSITIVE) &&
+                                (aaSeqStartPosition == exon.getCdStart())) || ((gene.getStrand() == Strand.NEGATIVE) &&
+                                (aaSeqStartPosition == exon.getCdEnd() - 3))))) {
                             bgGraphics = context.getGraphic2DForColor(Color.green);
                         } else if (acid.getSymbol() == 'X') {
                             bgGraphics = context.getGraphic2DForColor(Color.RED);
@@ -496,10 +541,8 @@ public class BasicFeatureRenderer extends FeatureRenderer {
                 // The last codon is not complete (continues on next exon).
                 // TODO -- get base from previous exon and compute aaSequence
                 int cdEnd = Math.min(exon.getCdEnd(), exon.getEnd());
-                aaRect.x = getPixelFromChromosomeLocation(exon.getChromosome(), aaSeqStartPosition,
-                        theOrigin, locationScale);
-                aaRect.width = getPixelFromChromosomeLocation(exon.getChromosome(), cdEnd,
-                        theOrigin, locationScale) - aaRect.x;
+                aaRect.x = getPixelFromChromosomeLocation(exon.getChr(), aaSeqStartPosition, theOrigin, locationScale);
+                aaRect.width = getPixelFromChromosomeLocation(exon.getChr(), cdEnd, theOrigin, locationScale) - aaRect.x;
                 Graphics2D bgGraphics = context.getGraphic2DForColor(odd ? AA_COLOR_1 : AA_COLOR_2);
                 odd = !odd;
                 bgGraphics.fill(aaRect);
@@ -516,33 +559,61 @@ public class BasicFeatureRenderer extends FeatureRenderer {
         return "Basic Feature";
     }
 
-    static OpenIntObjectHashMap greyscaleCache = new OpenIntObjectHashMap(500);
+    // Declare this for efficiency.  Note: this makes getFeatureColor(...) not thread safe.
+    private float[] colorComps = new float[3];
 
-    private Color getFeatureColor(Feature feature, Track track) {
+    private Color getFeatureColor(Feature feature, FeatureTrack track) {
         // Set color used to draw the feature
-        Color color = feature.getColor();
+
+        Color color = null;
+        if (track.isItemRGB()) {
+            color = feature.getColor();
+        }
+
         if (color == null) {
             // TODO -- hack, generalize this
             if (track.getTrackType() == TrackType.CNV) {
                 color = feature.getName().equals("gain") ? DULL_RED : DULL_BLUE;
-            } else if (feature.hasScore() && feature instanceof DRangerFeature) {
-                //TODO -- experimenting with this for DRanger only.  Remove instanceOf if it works out
-                int shade = (int) (226 - (226.0 / 834.0) * (feature.getScore()));
-                shade = Math.max(0, Math.min(226, shade));
-                color = (Color) greyscaleCache.get(shade);
-                if (color == null) {
-                    color = new Color(shade, shade, shade);
-                    greyscaleCache.put(shade, color);
-                }
             } else {
                 // Only used if feature color is not set
                 color = track.getColor();
             }
 
         }
+
+        if (track.isUseScore()) {
+            float min = getViewLimitMin(track);
+            float max = getViewLimitMax(track);
+            float score = feature.getScore();
+            float alpha = Float.isNaN(score) ? 1 : getAlpha(min, max, score);
+            color.getColorComponents(colorComps);
+            color = ColorUtilities.getCompositeColor(colorComps, alpha);
+        }
+
         return color;
     }
 
+    float getViewLimitMin(FeatureTrack track) {
+        if (Float.isNaN(viewLimitMin)) {
+            viewLimitMin = Float.isNaN(track.getViewLimitMin()) ? 0 : track.getViewLimitMin();
+        }
+        return viewLimitMin;
+    }
+
+    float getViewLimitMax(FeatureTrack track) {
+        if (Float.isNaN(viewLimitMax)) {
+            viewLimitMax = Float.isNaN(track.getViewLimitMax()) ? 1000 : track.getViewLimitMax();
+        }
+        return viewLimitMax;
+    }
+
+    private float getAlpha(float minRange, float maxRange, float value) {
+        float binWidth = (maxRange - minRange) / 9;
+        int binNumber = (int) ((value - minRange) / binWidth);
+        return Math.min(1.0f, 0.2f + (binNumber * 0.8f) / 9);
+    }
+
+
     private int getLastLargestMaxY() {
 
         double largestY = Math.max(lastFeatureLineMaxY,
@@ -555,4 +626,5 @@ public class BasicFeatureRenderer extends FeatureRenderer {
                                                  double locationScale) {
         return (int) Math.round((chromosomeLocation - origin) / locationScale);
     }
+
 }
diff --git a/src/org/broad/igv/renderer/ColorScale.java b/src/org/broad/igv/renderer/ColorScale.java
index d7e258f..1e66202 100644
--- a/src/org/broad/igv/renderer/ColorScale.java
+++ b/src/org/broad/igv/renderer/ColorScale.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/renderer/ColorScaleFactory.java b/src/org/broad/igv/renderer/ColorScaleFactory.java
index 8f4b689..4594833 100644
--- a/src/org/broad/igv/renderer/ColorScaleFactory.java
+++ b/src/org/broad/igv/renderer/ColorScaleFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/renderer/ContinuousColorScale.java b/src/org/broad/igv/renderer/ContinuousColorScale.java
index 6a6dc58..69f77fa 100644
--- a/src/org/broad/igv/renderer/ContinuousColorScale.java
+++ b/src/org/broad/igv/renderer/ContinuousColorScale.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -25,7 +25,7 @@ package org.broad.igv.renderer;
 
 //~--- non-JDK imports --------------------------------------------------------
 
-import org.broad.igv.IGVConstants;
+import org.broad.igv.ui.UIConstants;
 import org.broad.igv.util.ColorUtilities;
 
 import java.awt.*;
@@ -52,7 +52,7 @@ public class ContinuousColorScale extends AbstractColorScale {
     private Color midColor;
     private Color maxColor;
     private Color[] colors;
-    private Color noDataColor = IGVConstants.NO_DATA_COLOR;
+    private Color noDataColor = UIConstants.NO_DATA_COLOR;
     private boolean defaultCS = false;
 
 
diff --git a/src/org/broad/igv/renderer/CosmicFeatureRenderer.java b/src/org/broad/igv/renderer/CosmicFeatureRenderer.java
new file mode 100644
index 0000000..689ae35
--- /dev/null
+++ b/src/org/broad/igv/renderer/CosmicFeatureRenderer.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.renderer;
+
+import org.apache.log4j.Logger;
+import org.broad.igv.feature.Feature;
+import org.broad.igv.track.FeatureTrack;
+import org.broad.igv.track.RenderContext;
+import org.broad.igv.ui.FontManager;
+import org.broad.igv.ui.util.ColorTable;
+import org.broad.igv.PreferenceManager;
+
+import java.awt.*;
+
+public class CosmicFeatureRenderer extends FeatureRenderer {
+
+    private static Logger log = Logger.getLogger(CosmicFeatureRenderer.class);
+    ColorTable colorScheme;
+
+    public CosmicFeatureRenderer() {
+        colorScheme = PreferenceManager.getInstance().getMutationColorScheme();
+    }
+
+    public String getDisplayName() {
+        return "Mutation";
+    }
+
+    static Font font = FontManager.getScalableFont(12);
+
+    /**
+     * Note:  assumption is that featureList is sorted by start position.
+     */
+    public void renderFeatures(java.util.List<Feature> featureList, RenderContext context,
+                               Rectangle trackRectangle, FeatureTrack track) {
+
+        double origin = context.getOrigin();
+        double locScale = context.getScale();
+
+        if (featureList != null && featureList.size() > 0) {
+
+
+            Rectangle lastRect = null;
+            for (Feature feature : featureList) {
+
+                // Note -- don't cast these to an int until the range is checked.
+                // could get an overflow.
+                double pixelStart = Math.round((feature.getStart() - origin) / locScale);
+                double pixelEnd = Math.round((feature.getEnd() - origin) / locScale);
+
+                // If the any part of the feature fits in the
+                // Track rectangle draw it
+                if (pixelEnd >= trackRectangle.getX() &&
+                        pixelStart <= trackRectangle.getMaxX()) {
+
+                    // Set color used to draw the feature
+
+
+                    int width = (int) pixelEnd - (int) pixelStart;
+                    if (width < 3) {
+                        width = 3;
+                    }
+
+                    int mutHeight = (int) Math.max(1, trackRectangle.getHeight() - 2);
+                    int mutY = (int) (trackRectangle.getY() + (trackRectangle.getHeight() - mutHeight) / 2);
+
+                    Rectangle mutRect = new Rectangle((int) pixelStart, mutY, width, mutHeight);
+
+                    Color color = colorScheme.get(feature.getType());
+
+                    
+                    Graphics2D g = context.getGraphic2DForColor(color);
+                    g.setFont(font);
+
+                    if (getOverlayMode() == true) {
+                        g.draw(mutRect);
+                        mutRect.x--;
+                        mutRect.width += 2;
+                        g.draw(mutRect);
+
+                    } else {
+                        g.fill(mutRect);
+                        if (lastRect != null && mutRect.intersects(lastRect)) {
+                            // Indicate overlapping mutations
+                            Graphics2D g2 = context.getGraphic2DForColor(Color.BLACK);
+                            g2.draw(mutRect);
+
+                        }
+                    }
+
+                    lastRect = mutRect;
+                }
+            }
+        }
+    }
+
+}
diff --git a/src/org/broad/igv/renderer/CytobandRenderer.java b/src/org/broad/igv/renderer/CytobandRenderer.java
index a4f36ea..b34c98c 100644
--- a/src/org/broad/igv/renderer/CytobandRenderer.java
+++ b/src/org/broad/igv/renderer/CytobandRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -27,10 +27,9 @@
 package org.broad.igv.renderer;
 
 import org.broad.igv.feature.Cytoband;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.ui.FontManager;
 import org.broad.igv.ui.IGVMainFrame;
-import org.broad.igv.ui.IGVModel;
-import org.broad.igv.ui.ViewContext;
 
 import java.awt.*;
 import java.awt.geom.AffineTransform;
@@ -146,7 +145,7 @@ public class CytobandRenderer {
 
     //Refactor -- remove reference to IGVMainFrame
     private ViewContext getViewContext() {
-        return IGVModel.getInstance().getViewContext();
+        return ViewContext.getInstance();
     }
 
     public void applyTextTranslationAndRotation(Graphics2D graphics2, int x, int y) {
diff --git a/src/org/broad/igv/renderer/DASFeatureRenderer.java b/src/org/broad/igv/renderer/DASFeatureRenderer.java
deleted file mode 100644
index c369796..0000000
--- a/src/org/broad/igv/renderer/DASFeatureRenderer.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package org.broad.igv.renderer;
-
-import org.broad.igv.feature.Feature;
-
-import java.awt.Color;
-import java.awt.Font;
-import java.awt.Graphics2D;
-import java.awt.Rectangle;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.broad.igv.PreferenceManager;
-import org.broad.igv.feature.Mutation;
-import org.broad.igv.track.RenderContext;
-import org.broad.igv.track.Track;
-import org.broad.igv.ui.FontManager;
-import org.broad.igv.renderer.GraphicUtils;
-/*
-Copyright (c) 2007-$today.year by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
-All Rights Reserved.
-
-This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
-is available at http://www.opensource.org/licenses/lgpl-2.1.php.
-
-THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
-ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
-OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
-RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
-ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
-DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
-BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
-FOREGOING.
-*/
-
-public class DASFeatureRenderer extends FeatureRenderer {
-
-    private static Logger log = Logger.getLogger(MutationRenderer.class);
-
-    public String getDisplayName() {
-        return "Mutation";
-    }
-
-    static Font font = FontManager.getScalableFont(12);
-
-    /**
-     * Note:  assumption is that featureList is sorted by start position.
-     *
-     * @param featureList
-     * @param g2D
-     * @param rect
-     * @param originShift
-     * @param locScale
-     */
-    public void renderFeatures(java.util.List<Feature> featureList, RenderContext context,
-                               Rectangle trackRectangle, Track track) {
-
-        double origin = context.getOrigin();
-        double locScale = context.getScale();
-
-        if (featureList != null && featureList.size() > 0) {
-
-
-            Rectangle lastRect = null;
-            for (Feature feature : featureList) {
-
-                // Note -- don't cast these to an int until the range is checked.
-                // could get an overflow.
-                double pixelStart = Math.round((feature.getStart() - origin) / locScale);
-                double pixelEnd = Math.round((feature.getEnd() - origin) / locScale);
-
-                // If the any part of the feature fits in the
-                // Track rectangle draw it
-                if (pixelEnd >= trackRectangle.getX() &&
-                        pixelStart <= trackRectangle.getMaxX()) {
-
-                    // Set color used to draw the feature
-                    Color color = feature.getColor();
-                    Graphics2D g = context.getGraphic2DForColor(color);
-                    g.setFont(font);
-
-
-                    int width = (int)pixelEnd - (int)pixelStart;
-                    if (width < 3) {
-                        width = 3;
-                    }
-
-                    int mutHeight = (int) Math.max(1, trackRectangle.getHeight() - 2);
-                    int mutY = (int) (trackRectangle.getY() + (trackRectangle.getHeight() - mutHeight) / 2);
-
-                    Rectangle mutRect = new Rectangle((int) pixelStart, mutY, width, mutHeight);
-
-                    if (getOverlayMode() == true) {
-                        Graphics2D gRect = (PreferenceManager.getInstance().getColorOverlay() ? g :
-                                context.getGraphic2DForColor(Color.BLACK));
-                        gRect.draw(mutRect);
-                        mutRect.x--;
-                        mutRect.width += 2;
-                        gRect.draw(mutRect);
-
-                    } else {
-                        g.fill(mutRect);
-                        if (lastRect != null && mutRect.intersects(lastRect)) {
-                            // Indicate overlapping mutations
-                            Graphics2D g2 = context.getGraphic2DForColor(Color.BLACK);
-                            g2.draw(mutRect);
-
-                        }
-                    }
-
-                    lastRect = mutRect;
-                }
-            }
-        }
-    }
-
-}
diff --git a/src/org/broad/igv/renderer/DataRange.java b/src/org/broad/igv/renderer/DataRange.java
index 32abff7..20b107c 100644
--- a/src/org/broad/igv/renderer/DataRange.java
+++ b/src/org/broad/igv/renderer/DataRange.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,15 +23,19 @@
 */
 package org.broad.igv.renderer;
 
+import org.broad.igv.session.Persistable;
+
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * Encapsulates parameter for an x-y plot axis.
  *
  * @author jrobinso
  */
-public class DataRange {
+public class DataRange implements Persistable {
 
-
-    enum Type {
+    public enum Type {
         LOG, LINEAR
     }
 
@@ -88,47 +92,34 @@ public class DataRange {
         this.drawBaseline = drawBaseline;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+    public void setType(Type type) {
+        this.type = type;
+    }
+
     public Type getType() {
         return type;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+    public boolean isLog() {
+        return type == Type.LOG;
+    }
+
+
     public float getMinimum() {
         return minimum;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public float getBaseline() {
         return baseline;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public float getMaximum() {
         return maximum;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public boolean isFlipAxis() {
         return flipAxis;
     }
@@ -141,5 +132,45 @@ public class DataRange {
         this.drawBaseline = drawBaseline;
     }
 
+    //   Persistable interface
+
+    public Map<String, String> getPersistentState() {
+        Map<String, String> attributes = new HashMap();
+        attributes.put("type", type.toString());
+        attributes.put("minimum", String.valueOf(minimum));
+        attributes.put("baseline", String.valueOf(baseline));
+        attributes.put("maximum", String.valueOf(maximum));
+        attributes.put("flipAxis", String.valueOf(flipAxis));
+        attributes.put("drawBaseline", String.valueOf(drawBaseline));
+        return attributes;
+    }
 
+    public void restorePersistentState(Map<String, String> values) {
+
+        //TODO Go through generically and set the types
+        String typeString = values.get("type");
+        if (typeString != null) {
+            type = Type.valueOf(typeString);
+        }
+        String minimumString = values.get("minimum");
+        if (minimumString != null) {
+            minimum = Float.parseFloat(minimumString);
+        }
+        String baselineString = values.get("baseline");
+        if (typeString != null){
+            baseline = Float.parseFloat(baselineString);
+        }
+        String maximumString = values.get("maximum");
+        if (typeString != null){
+            maximum = Float.parseFloat(maximumString);
+        }
+        String flipAxisString = values.get("flipAxis");
+        if (typeString != null){
+            flipAxis = Boolean.parseBoolean(flipAxisString);
+        }
+        String drawBaselineString = values.get("drawBaseline");
+        if (drawBaselineString != null){
+            drawBaseline = Boolean.parseBoolean(drawBaselineString);
+        }
+    }
 }
diff --git a/src/org/broad/igv/renderer/DataRenderer.java b/src/org/broad/igv/renderer/DataRenderer.java
index 3e05f12..9f866f3 100644
--- a/src/org/broad/igv/renderer/DataRenderer.java
+++ b/src/org/broad/igv/renderer/DataRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -30,7 +30,7 @@ package org.broad.igv.renderer;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
+import org.broad.igv.Globals;
 import org.broad.igv.PreferenceManager;
 import org.broad.igv.feature.LocusScore;
 import org.broad.igv.track.RenderContext;
@@ -44,11 +44,13 @@ import java.util.List;
  */
 public abstract class DataRenderer implements Renderer {
 
+    private static Logger log = Logger.getLogger(DataRenderer.class);
+    private static Color defaultColor = new Color(0, 0, 150);
+
     protected static final int AXIS_AREA_WIDTH = 60;
     protected static Color axisLineColor = new Color(255, 180, 180);
+
     private boolean overlayMode;
-    private static Logger log = Logger.getLogger(DataRenderer.class);
-    private static Color defaultColor = new Color(0, 0, 150);
 
     /** Field description */
 
@@ -119,7 +121,7 @@ public abstract class DataRenderer implements Renderer {
                 PreferenceManager.getInstance().getChartPreferences();
 
         // For now disable axes for all chromosome view
-        if (context.getChr().equals(IGVConstants.CHR_ALL)) {
+        if (context.getChr().equals(Globals.CHR_ALL)) {
             return;
         }
         if (prefs.isDrawAxis()) {
@@ -149,4 +151,5 @@ public abstract class DataRenderer implements Renderer {
 
     protected abstract void renderScores(Track track, List<LocusScore> scores,
                                          RenderContext context, Rectangle arect);
+
 }
diff --git a/src/org/broad/igv/renderer/FeatureDensityRenderer.java b/src/org/broad/igv/renderer/FeatureDensityRenderer.java
index b299570..e6fd9d6 100644
--- a/src/org/broad/igv/renderer/FeatureDensityRenderer.java
+++ b/src/org/broad/igv/renderer/FeatureDensityRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/renderer/FeatureRenderer.java b/src/org/broad/igv/renderer/FeatureRenderer.java
index 826e0af..e2e875c 100644
--- a/src/org/broad/igv/renderer/FeatureRenderer.java
+++ b/src/org/broad/igv/renderer/FeatureRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -26,6 +26,7 @@ import org.apache.log4j.Logger;
 import org.broad.igv.feature.Feature;
 import org.broad.igv.track.RenderContext;
 import org.broad.igv.track.Track;
+import org.broad.igv.track.FeatureTrack;
 
 import java.awt.*;
 import java.util.HashMap;
@@ -47,6 +48,8 @@ public abstract class FeatureRenderer implements Renderer {
      */
     private static Logger log = Logger.getLogger(FeatureRenderer.class);
 
+    private Feature highlightFeature = null;
+
 
     public void setOverlayMode(boolean overlayMode) {
         this.overlayMode = overlayMode;
@@ -84,5 +87,13 @@ public abstract class FeatureRenderer implements Renderer {
     }
 
     public abstract void renderFeatures(List<Feature> features, RenderContext context,
-                                        Rectangle rect, Track track);
+                                        Rectangle rect, FeatureTrack track);
+
+    public Feature getHighlightFeature() {
+        return highlightFeature;
+    }
+
+    public void setHighlightFeature(Feature highlightFeature) {
+        this.highlightFeature = highlightFeature;
+    }
 }
diff --git a/src/org/broad/igv/renderer/GeneTrackRenderer.java b/src/org/broad/igv/renderer/GeneTrackRenderer.java
index 6794a3e..baa3a24 100644
--- a/src/org/broad/igv/renderer/GeneTrackRenderer.java
+++ b/src/org/broad/igv/renderer/GeneTrackRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/renderer/GisticTrackRenderer.java b/src/org/broad/igv/renderer/GisticTrackRenderer.java
index 39d7b38..eb09ee5 100644
--- a/src/org/broad/igv/renderer/GisticTrackRenderer.java
+++ b/src/org/broad/igv/renderer/GisticTrackRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/renderer/GraphicUtils.java b/src/org/broad/igv/renderer/GraphicUtils.java
index 1c2e0bb..aa99db6 100644
--- a/src/org/broad/igv/renderer/GraphicUtils.java
+++ b/src/org/broad/igv/renderer/GraphicUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,6 +23,8 @@ package org.broad.igv.renderer;
 
 //~--- JDK imports ------------------------------------------------------------
 
+import org.broad.igv.Globals;
+
 import java.awt.*;
 import java.awt.geom.Rectangle2D;
 import java.util.ArrayList;
@@ -92,26 +94,11 @@ public class GraphicUtils {
 
     }
 
-    /**
-     * Draw a block of text centered verticallyin the rectangle
-     *
-     * @param text
-     * @param rect
-     * @param g2D
-     */
-    public static void drawVerticallyCenteredText(String text, int margin, Rectangle rect,
-                                                  Graphics g2D, boolean rightJustify) {
-        FontMetrics fontMetrics = g2D.getFontMetrics();
-        Rectangle2D textBounds = fontMetrics.getStringBounds(text, g2D);
-        int yOffset = (int) ((rect.getHeight() - textBounds.getHeight()) / 2);
-        int yPos = (rect.y + rect.height) - yOffset - (int) (textBounds.getHeight() / 4);
-        if (rightJustify) {
-            drawRightJustifiedText(text, rect.x + rect.width - margin, yPos, g2D);
-        } else {
-            g2D.drawString(text, margin, yPos);
-        }
+    public static void drawVerticallyCenteredText(String text, int margin, Rectangle rect, Graphics g2D, boolean rightJustify) {
+        drawVerticallyCenteredText(text, margin, rect, g2D, rightJustify, false);
     }
 
+
     /**
      * Draw a block of text centered verticallyin the rectangle
      *
@@ -119,40 +106,32 @@ public class GraphicUtils {
      * @param rect
      * @param g2D
      */
-    static Pattern splitPattern = Pattern.compile("[,_\\s]+");
-
-    public static void drawVerticallyCenteredWrappedText(String text, int margin, Rectangle rect,
-                                                         Graphics g2D) {
-
+    public static void drawVerticallyCenteredText
+            (String text,
+             int margin,
+             Rectangle rect,
+             Graphics g2D,
+             boolean rightJustify,
+             boolean clear) {
         FontMetrics fontMetrics = g2D.getFontMetrics();
         Rectangle2D textBounds = fontMetrics.getStringBounds(text, g2D);
+
         int yOffset = (int) ((rect.getHeight() - textBounds.getHeight()) / 2);
         int yPos = (rect.y + rect.height) - yOffset - (int) (textBounds.getHeight() / 4);
 
-        if (textBounds.getWidth() > rect.width - margin) {
-            List<String> strings = new ArrayList();
-            String[] words = splitPattern.split(text);
-            String nextString = words[0];
-            for (int i = 1; i < words.length; i++) {
-                Rectangle2D bounds = fontMetrics.getStringBounds(nextString + words[i], g2D);
-                if (bounds.getWidth() > rect.width - margin) {
-                    strings.add(nextString);
-                    nextString = words[i];
-                } else {
-                    nextString += words[i];
-                }
-            }
-            strings.add(nextString);
-
-            int gap = 5;
-            int nLevels = Math.min(strings.size(), (int) (rect.getHeight() / (textBounds.getHeight() + gap))) + 1;
-            yPos -= (nLevels * (textBounds.getHeight() + gap) / 2);
-
-            for (int i = 0; i < nLevels; i++) {
-                g2D.drawString(strings.get(i), margin, yPos);
-                yPos += (int) (textBounds.getHeight() + gap);
-            }
+        if (clear) {
+            int h = 2 * (int) textBounds.getHeight();
+            //Color c = g2D.getColor();
+            //Globals.isHeadless();
+            //g2D.setColor(Globals.VERY_LIGHT_GREY);
+            int y = Math.max(rect.y, yPos - h);
+            int h2 = Math.min(rect.height, 2*h);
+            g2D.clearRect(rect.x, y, rect.width, h2);
+            //g2D.setColor(c);
+        }
 
+        if (rightJustify) {
+            drawRightJustifiedText(text, rect.x + rect.width - margin, yPos, g2D);
         } else {
             g2D.drawString(text, margin, yPos);
         }
@@ -166,6 +145,7 @@ public class GraphicUtils {
      * @param y
      * @param g
      */
+
     public static void drawRightJustifiedText(String text, int right, int y,
                                               Graphics g) {
         FontMetrics fontMetrics = g.getFontMetrics();
@@ -183,11 +163,38 @@ public class GraphicUtils {
                 BasicStroke.JOIN_BEVEL, 1.0f, // join style, miter limit
                 new float[]{8.0f, 3.0f, 2.0f, 3.0f}, // the dash pattern :  on 8, off 3, on 2, off 3
                 0.0f);  // the dash phase
-        drawDashedLine(g, thindashed, x1,
-                y1, x2, y2);
+        drawDashedLine(g, thindashed, x1, y1, x2, y2);
 
     }
 
+    public static void drawWrappedText(String string, Rectangle rect, Graphics2D g2D, boolean clear) {
+        FontMetrics fontMetrics = g2D.getFontMetrics();
+        Rectangle2D stringBounds = fontMetrics.getStringBounds(string, g2D);
+        int textHeight = (int) stringBounds.getHeight() + 5;
+        double textWidth = stringBounds.getWidth() + 10;
+        if (textWidth < rect.width) {
+            GraphicUtils.drawVerticallyCenteredText(string, 5, rect, g2D, false, clear);
+        } else {
+            int charWidth = fontMetrics.charWidth('a');
+            int charsPerLine = rect.width / charWidth;
+            int nStrings = (string.length() / charsPerLine) + 1;
+            if (nStrings * textHeight > rect.height) {
+                GraphicUtils.drawVerticallyCenteredText(string, 5, rect, g2D, false, clear);
+            } else {
+                int breakPoint = 0;
+                Rectangle tmp = new Rectangle(rect);
+                tmp.y -= ((nStrings - 1) * textHeight) / 2;
+                while (breakPoint < string.length()) {
+                    int end = Math.min(string.length(), breakPoint + charsPerLine);
+                    GraphicUtils.drawVerticallyCenteredText(string.substring(breakPoint, end), 5, tmp, g2D, false);
+                    breakPoint += charsPerLine;
+                    tmp.y += textHeight;
+                }
+            }
+        }
+    }
+
+
     /**
      * Method description
      * Stroke thindashed = new BasicStroke(thickness, // line width
@@ -197,7 +204,6 @@ public class GraphicUtils {
      * phase);  // the dash phase
      *
      * @param g
-     * @param thickness
      */
     public static void drawDashedLine(Graphics2D g, Stroke stroke,
                                       int x1, int y1, int x2, int y2) {
diff --git a/src/org/broad/igv/renderer/HeatmapRenderer.java b/src/org/broad/igv/renderer/HeatmapRenderer.java
index 1df227a..bc093fc 100644
--- a/src/org/broad/igv/renderer/HeatmapRenderer.java
+++ b/src/org/broad/igv/renderer/HeatmapRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -17,13 +17,12 @@
  */
 package org.broad.igv.renderer;
 
-//~--- non-JDK imports --------------------------------------------------------
-
-import org.broad.igv.IGVConstants;
+import org.broad.igv.Globals;
 import org.broad.igv.PreferenceManager;
-import org.broad.igv.data.Segment;
+import org.broad.igv.data.seg.Segment;
 import org.broad.igv.data.rnai.RNAIGeneScore;
 import org.broad.igv.feature.LocusScore;
+import org.broad.igv.track.DataSourceTrack;
 import org.broad.igv.track.RenderContext;
 import org.broad.igv.track.Track;
 import org.broad.igv.track.TrackType;
@@ -38,40 +37,12 @@ import java.util.Map;
  */
 public class HeatmapRenderer extends DataRenderer {
 
-    static double log2 = Math.log(2);
-    /**
-     * A cache for "low confidence color", which is a function of zoom level.
-     */
-    static Map<Integer, Color> lowConfColorCache = new Hashtable();
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public String getDisplayName() {
         return "Heatmap";
     }
 
     /**
-     * Return the color indicating a low confdence score as a function of zoom level.  Currently
-     * this is only used with RNAi data.
-     *
-     * @param zoom
-     * @return
-     */
-    private static Color getLowConfColor(int zoom) {
-        Color lowConfColor = lowConfColorCache.get(zoom);
-        if (lowConfColor == null) {
-
-            int value = 225 - Math.min(70, zoom * 10);
-            lowConfColor = new Color(value, value, value);
-            lowConfColorCache.put(zoom, lowConfColor);
-        }
-        return lowConfColor;
-    }
-
-    /**
      * Render the data track as a heat map.
      * <p/>
      * This method has gotten quite complicated,  most of it from the option to join adjacent
@@ -82,8 +53,7 @@ public class HeatmapRenderer extends DataRenderer {
      * @param context
      * @param rect
      */
-    public void renderScores(Track track, List<LocusScore> scores, RenderContext context,
-                             Rectangle rect) {
+    public void renderScores(Track track, List<LocusScore> scores, RenderContext context, Rectangle rect) {
 
         ContinuousColorScale colorScale = track.getColorScale();
 
@@ -103,6 +73,7 @@ public class HeatmapRenderer extends DataRenderer {
 
         int lastPEnd = 0;
         int lastPStart = 0;
+        int lastW = 0;
         Color lastColor = null;
         for (LocusScore score : scores) {
             if (lastPStart > maxX) {
@@ -111,16 +82,25 @@ public class HeatmapRenderer extends DataRenderer {
 
             // Note -- don't cast these to an int until the range is checked,
             // otherwise could get an overflow.
-            double pStart = Math.round((score.getStart() - origin) / locScale);
-            double dx = Math.ceil((score.getEnd() - score.getStart()) / locScale) + 1;
-            float dataY = scaleData(track, score.getScore());
+            int pStart = (int) ((score.getStart() - origin) / locScale);
+            int pEnd = (int) ((score.getEnd() - origin) / locScale);
+            int w = pEnd - pStart;
+            if(track.getTrackType() == TrackType.CNV) {
+                w = Math.max(1, w);
+            }
+
+            float dataY = track.logScaleData(score.getScore());
             Color graphColor = colorScale.getColor(dataY);
-            int px = (int) pStart;
-            int w = (int) dx;
 
 
-            if ((pStart + dx) >= 0 && (lastPStart <= maxX)) {
+            if ((pStart + w) >= 0 && (lastPStart <= maxX)) {
 
+                // This test handles the rather pathological case where the previous feature was 1 pixel wide, and
+                // the current feature overlaps it because of roundoff error when scaling.  
+                if (pStart < lastPEnd && w > 1 && lastW == 1) {
+                    pStart++;
+                    w--;
+                }
 
                 // TODo The instanceof test is very very ugly.   Special RNAi treatment
                 // Refactor  to generalize "confidence" for all datasets
@@ -133,47 +113,49 @@ public class HeatmapRenderer extends DataRenderer {
                     }
 
                     Graphics2D g2D = context.getGraphic2DForColor(graphColor);
-                    if (px < maxX) {
-                        if (w <= 1) {
-                            g2D.drawLine(px, minY, px, maxY);
-                        } else {
-                            g2D.fillRect(px, minY, w, height);
-                        }
+                    if (pStart < maxX) {
+                        // Clip at edges
+                        int pLeft = Math.max(rect.x,  pStart);
+                        int pRight = Math.min(rect.x + rect.width, pStart + w);
+                        int adjustedW = pRight - pLeft;
+                        //if (adjustedW == 1) {
+                        //    g2D.drawLine(pRight, minY, pRight, maxY);
+                        //} else if (w > 1) {
+                            g2D.fillRect(pLeft, minY, adjustedW, height);
+                        //}
                     }
 
                     // Segmented copy numbers (score type "segment") can be optionally joined
                     if (score instanceof Segment &&
                             PreferenceManager.getInstance().isJoinAdjacentSegments() &&
-                            lastColor != null && (px - lastPEnd) > 1) {
+                            lastColor != null && (pStart - lastPEnd) > 1) {
 
-                        int midPoint = (px + lastPEnd) / 2;
+                        int midPoint = (pStart + lastPEnd) / 2;
 
                         context.getGraphic2DForColor(lastColor).fillRect(lastPEnd, minY,
                                 midPoint - lastPEnd, height);
-                        g2D.fillRect(midPoint, minY, px - midPoint, height);
+                        g2D.fillRect(midPoint, minY, pStart - midPoint, height);
 
                         // Cross hatch joined area  --- don't do for whole chromosome
-                        if (!context.getChr().equals(IGVConstants.CHR_ALL)) {
-                            if (px - lastPEnd > 4 && height > 2) {
+                        if (!context.getChr().equals(Globals.CHR_ALL)) {
+                            if (pStart - lastPEnd > 4 && height > 2) {
                                 Color c = new Color(0.4f, 0.4f, 0.4f, 0.5f);
                                 Graphics2D gLine = context.getGraphic2DForColor(c);
                                 int midpoint = minY + height / 2;
                                 if (height > 4) {
                                     gLine.drawLine(lastPEnd, minY + 3, lastPEnd, minY + height - 3);
-                                    gLine.drawLine(px, minY + 3, px, minY + height - 3);
+                                    gLine.drawLine(pStart, minY + 3, pStart, minY + height - 3);
                                 }
-                                gLine.drawLine(lastPEnd, minY + height / 2, px - 1, minY + height / 2);
+                                gLine.drawLine(lastPEnd, minY + height / 2, pStart - 1, minY + height / 2);
                             }
                         }
                     }
                 }
-
             }
-            lastPStart = px;
-            lastPEnd = px + w;
+            lastPStart = pStart;
+            lastPEnd = pStart + w;
+            lastW = w;
             lastColor = graphColor;
-
-
         }
     }
 
@@ -186,32 +168,27 @@ public class HeatmapRenderer extends DataRenderer {
     }
 
     /**
-     * Special normalization function for linear (non logged) copy number data
+     * Return the color indicating a low confdence score as a function of zoom level.  Currently
+     * this is only used with RNAi data.
      *
-     * @param value
-     * @param norm
+     * @param zoom
      * @return
      */
-    float getLogNormalizedValue(float value, double norm) {
-        if (norm == 0) {
-            return Float.NaN;
-        } else {
-            return (float) (Math.log(Math.max(Float.MIN_VALUE, value) / norm) / log2);
+    private static Color getLowConfColor(int zoom) {
+        Color lowConfColor = lowConfColorCache.get(zoom);
+        if (lowConfColor == null) {
+
+            int value = 225 - Math.min(70, zoom * 10);
+            lowConfColor = new Color(value, value, value);
+            lowConfColorCache.put(zoom, lowConfColor);
         }
+        return lowConfColor;
     }
 
-    private float scaleData(Track track, float dataY) {
-
-        // Special case for copy # -- centers data around 2 copies (1 for allele
-        // specific) and log normalizes
-        if (((track.getTrackType() == TrackType.COPY_NUMBER) || (track.getTrackType() == TrackType.ALLELE_SPECIFIC_COPY_NUMBER)) && !track.isLogNormalized()) {
-            double centerValue = (track.getTrackType() == TrackType.ALLELE_SPECIFIC_COPY_NUMBER)
-                    ? 1.0 : 2.0;
-
-            dataY = getLogNormalizedValue(dataY, centerValue);
-        }
+    /**
+     * A cache for "low confidence color", which is a function of zoom level.
+     */
+    static Map<Integer, Color> lowConfColorCache = new Hashtable();
 
 
-        return dataY;
-    }
 }
diff --git a/src/org/broad/igv/renderer/LineplotRenderer.java b/src/org/broad/igv/renderer/LineplotRenderer.java
index bae618a..b125e26 100644
--- a/src/org/broad/igv/renderer/LineplotRenderer.java
+++ b/src/org/broad/igv/renderer/LineplotRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/renderer/MappedColorScale.java b/src/org/broad/igv/renderer/MappedColorScale.java
index ab0dce1..f3cc776 100644
--- a/src/org/broad/igv/renderer/MappedColorScale.java
+++ b/src/org/broad/igv/renderer/MappedColorScale.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,7 +23,7 @@
 
 package org.broad.igv.renderer;
 
-import org.broad.igv.IGVConstants;
+import org.broad.igv.ui.UIConstants;
 import org.broad.igv.util.ColorUtilities;
 
 import java.awt.*;
@@ -39,7 +39,7 @@ public class MappedColorScale extends AbstractColorScale {
 
     public static String serializationClassId = "MappedColorScale";
     Color defaultColor = Color.black;
-    Color noDataColor = IGVConstants.NO_DATA_COLOR;
+    Color noDataColor = UIConstants.NO_DATA_COLOR;
     Map<String, Color> colorMap;
     boolean defaultCS = false;
 
diff --git a/src/org/broad/igv/renderer/MutationRenderer.java b/src/org/broad/igv/renderer/MutationRenderer.java
index 7a64ea6..a436b09 100644
--- a/src/org/broad/igv/renderer/MutationRenderer.java
+++ b/src/org/broad/igv/renderer/MutationRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -21,10 +21,10 @@ package org.broad.igv.renderer;
 import org.apache.log4j.Logger;
 import org.broad.igv.PreferenceManager;
 import org.broad.igv.feature.Feature;
-import org.broad.igv.feature.Mutation;
+import org.broad.igv.track.FeatureTrack;
 import org.broad.igv.track.RenderContext;
-import org.broad.igv.track.Track;
 import org.broad.igv.ui.FontManager;
+import org.broad.igv.ui.IGVMainFrame;
 
 import java.awt.*;
 import java.util.List;
@@ -42,14 +42,9 @@ public class MutationRenderer extends FeatureRenderer {
     /**
      * Note:  assumption is that featureList is sorted by start position.
      *
-     * @param featureList
-     * @param g2D
-     * @param rect
-     * @param originShift
-     * @param locScale
      */
     public void renderFeatures(List<Feature> featureList, RenderContext context,
-                               Rectangle trackRectangle, Track track) {
+                               Rectangle trackRectangle, FeatureTrack track) {
 
         double origin = context.getOrigin();
         double locScale = context.getScale();
@@ -58,18 +53,19 @@ public class MutationRenderer extends FeatureRenderer {
 
 
             Rectangle lastRect = null;
+
+            boolean colorOverlay = IGVMainFrame.getInstance().getSession().getColorOverlay();
             for (Feature feature : featureList) {
 
 
                 // Note -- don't cast these to an int until the range is checked. 
                 // could get an overflow.
-                double pixelStart = ((feature.getStart() - 1 - origin) / locScale);
+                double pixelStart = ((feature.getStart() - origin) / locScale);
                 double pixelEnd = ((feature.getEnd() - origin) / locScale);
 
                 // If the any part of the feature fits in the 
                 // Track rectangle draw it
-                if (pixelEnd >= trackRectangle.getX() &&
-                        pixelStart <= trackRectangle.getMaxX()) {
+                if (pixelEnd >= trackRectangle.getX() && pixelStart <= trackRectangle.getMaxX()) {
 
                     // Set color used to draw the feature
 
@@ -84,15 +80,14 @@ public class MutationRenderer extends FeatureRenderer {
                         pixelStart--;
                     }
 
-                    int mutHeight = (int) Math.max(1, trackRectangle.getHeight() - 2);
+                    int mutHeight = (int) Math.max(1, trackRectangle.getHeight() - 1);
                     int mutY = (int) (trackRectangle.getY() + (trackRectangle.getHeight() - mutHeight) / 2);
 
 
                     Rectangle mutRect = new Rectangle((int) pixelStart, mutY, w, mutHeight);
 
                     if (getOverlayMode() == true) {
-                        Graphics2D gRect = (PreferenceManager.getInstance().getColorOverlay() ? g :
-                                context.getGraphic2DForColor(Color.BLACK));
+                        Graphics2D gRect = (colorOverlay ? g : context.getGraphic2DForColor(Color.BLACK));
                         gRect.draw(mutRect);
                         mutRect.x--;
                         mutRect.width += 2;
@@ -108,22 +103,6 @@ public class MutationRenderer extends FeatureRenderer {
                         }
                     }
 
-                    // Try to draw the new base, if there's room.  If there are any exceptions
-                    // fail silently (with a log message).   
-                    if (mutRect.getHeight() > 10 && mutRect.getWidth() > 10) {
-                        try {
-                            String newBase = ((Mutation) feature).getNewBase();
-                            if (newBase != null) {
-                                Graphics2D gText = context.getGraphic2DForColor(Color.black);
-                                font = FontManager.getScalableFont(10);
-                                GraphicUtils.drawCenteredText(newBase, mutRect, gText);
-                            }
-
-                        } catch (Exception exception) {
-                            log.error("Error drawing mutation base ", exception);
-                        }
-                    }
-
                     lastRect = mutRect;
                 }
             }
diff --git a/src/org/broad/igv/renderer/RNAiBarChartRenderer.java b/src/org/broad/igv/renderer/RNAiBarChartRenderer.java
index 8711e9b..156f496 100644
--- a/src/org/broad/igv/renderer/RNAiBarChartRenderer.java
+++ b/src/org/broad/igv/renderer/RNAiBarChartRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/renderer/Renderer.java b/src/org/broad/igv/renderer/Renderer.java
index e4bd28e..1928c12 100644
--- a/src/org/broad/igv/renderer/Renderer.java
+++ b/src/org/broad/igv/renderer/Renderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/renderer/RendererFactory.java b/src/org/broad/igv/renderer/RendererFactory.java
index b2e5a4f..36ed541 100644
--- a/src/org/broad/igv/renderer/RendererFactory.java
+++ b/src/org/broad/igv/renderer/RendererFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/renderer/ScatterplotRenderer.java b/src/org/broad/igv/renderer/ScatterplotRenderer.java
index 08b5528..7044dff 100644
--- a/src/org/broad/igv/renderer/ScatterplotRenderer.java
+++ b/src/org/broad/igv/renderer/ScatterplotRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/renderer/SequenceArray.java b/src/org/broad/igv/renderer/SequenceArray.java
index c81b27e..73efa0d 100644
--- a/src/org/broad/igv/renderer/SequenceArray.java
+++ b/src/org/broad/igv/renderer/SequenceArray.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/renderer/SequenceRenderer.java b/src/org/broad/igv/renderer/SequenceRenderer.java
index 0d5e732..668f66d 100644
--- a/src/org/broad/igv/renderer/SequenceRenderer.java
+++ b/src/org/broad/igv/renderer/SequenceRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -26,7 +26,7 @@ import org.broad.igv.feature.SequenceManager;
 import org.broad.igv.track.RenderContext;
 import org.broad.igv.track.Track;
 import org.broad.igv.ui.FontManager;
-import org.broad.igv.ui.IGVModel;
+import org.broad.igv.util.SOLIDUtils;
 
 import java.awt.*;
 import java.util.HashMap;
@@ -57,21 +57,32 @@ public class SequenceRenderer implements Renderer {
         nucleotideColors.put('n', Color.gray);
     }
 
+    public void draw(RenderContext context, Rectangle trackRectangle) {
+        draw(context, trackRectangle, false);
+    }
+
     // For efficiency
     //@Override
-    public void draw(RenderContext context, Rectangle trackRectangle) {
+    public void draw(RenderContext context, Rectangle trackRectangle, boolean showColorSpace) {
 
         double locScale = context.getScale();
         double origin = context.getOrigin();
         String chr = context.getChr();
-        String genome = IGVModel.getInstance().getViewContext().getGenomeId();
+        String genome = context.getGenomeId();
 
-        int start = (int) origin;
+        int start = Math.max(0, (int) origin - 1);
         int end = (int) (origin + trackRectangle.width * locScale) + 1;
         byte[] seq = SequenceManager.readSequence(genome, chr, start, end);
+        byte[] seqCS = null;
+        if (showColorSpace) {
+            seqCS = SOLIDUtils.convertToColorSpace(seq);
+        }
+
         if (seq != null && seq.length > 0) {
-            int pY = (int) trackRectangle.getY() + 2;
-            int dY = (int) trackRectangle.getHeight() - 4;
+            int hCS = (showColorSpace ? trackRectangle.height / 2 : 0);
+            int yBase = hCS + trackRectangle.y + 2;
+            int yCS = trackRectangle.y + 2;
+            int dY = (showColorSpace ? hCS : trackRectangle.height) - 4;
             int dX = (int) (1.0 / locScale);
             // Create a graphics to use
             Graphics2D g = (Graphics2D) context.getGraphics().create();
@@ -82,23 +93,28 @@ public class SequenceRenderer implements Renderer {
 
             // Loop through base pair coordinates
             for (int loc = start; loc < end; loc++) {
-
+                int idx = loc - start;
                 int pX0 = (int) ((loc - origin) / locScale);
-
-                char c = (char) seq[loc - start];
+                char c = (char) seq[idx];
                 Color color = nucleotideColors.get(c);
-
                 if (dX >= 8) {
                     if (color == null) {
                         color = Color.black;
                     }
                     g.setColor(color);
-                    drawCenteredText(g, new char[]{c}, pX0, pY + 2, dX, dY - 2);
+                    drawCenteredText(g, new char[]{c}, pX0, yBase + 2, dX, dY - 2);
+                    if (showColorSpace) {
+                        // draw color space #.  Color space is shifted to be between bases as it represents
+                        // two bases.
+                        g.setColor(Color.black);
+                        String cCS = String.valueOf(seqCS[idx]);
+                        drawCenteredText(g, cCS.toCharArray(), pX0 - dX/2, yCS + 2, dX, dY - 2);
+                    }
                 } else {
                     int bw = dX - 1;
                     if (color != null) {
                         g.setColor(color);
-                        g.fillRect(pX0, pY, bw, dY);
+                        g.fillRect(pX0, yBase, bw, dY);
                     }
 
                 }
diff --git a/src/org/broad/igv/renderer/TestGraphicUtils.java b/src/org/broad/igv/renderer/TestGraphicUtils.java
index 05e3557..78a388e 100644
--- a/src/org/broad/igv/renderer/TestGraphicUtils.java
+++ b/src/org/broad/igv/renderer/TestGraphicUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/renderer/VCFRenderer.java b/src/org/broad/igv/renderer/VCFRenderer.java
new file mode 100644
index 0000000..e632432
--- /dev/null
+++ b/src/org/broad/igv/renderer/VCFRenderer.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.renderer;
+
+import org.apache.log4j.Logger;
+import org.broad.igv.PreferenceManager;
+import org.broad.igv.feature.Feature;
+import org.broad.igv.track.tribble.VCFFeature;
+import org.broad.igv.track.FeatureTrack;
+import org.broad.igv.track.RenderContext;
+import org.broad.igv.ui.FontManager;
+import org.broad.igv.ui.util.ColorTable;
+
+import java.awt.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: May 18, 2010
+ * Time: 5:50:13 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class VCFRenderer extends FeatureRenderer {
+
+    private static Logger log = Logger.getLogger(CosmicFeatureRenderer.class);
+    ColorTable colorScheme;
+
+    public VCFRenderer() {
+        colorScheme = PreferenceManager.getInstance().getMutationColorScheme();
+    }
+
+    public String getDisplayName() {
+        return "Mutation";
+    }
+
+    static Font font = FontManager.getScalableFont(12);
+
+    static Color getColor(VCFFeature feature) {
+        if(feature.getInfo().contains("DB")) {
+            return Color.RED;
+        }
+        return Color.BLACK;
+    }
+
+    /**
+     * Note:  assumption is that featureList is sorted by start position.
+     */
+    public void renderFeatures(java.util.List<Feature> featureList, RenderContext context,
+                               Rectangle trackRectangle, FeatureTrack track) {
+
+        double origin = context.getOrigin();
+        double locScale = context.getScale();
+
+        if (featureList != null && featureList.size() > 0) {
+
+
+            Rectangle lastRect = null;
+            for (Feature feature : featureList) {
+
+                // Note -- don't cast these to an int until the range is checked.
+                // could get an overflow.
+                double pixelStart = Math.round((feature.getStart() - origin) / locScale);
+                double pixelEnd = Math.round((feature.getEnd() - origin) / locScale);
+
+                // If the any part of the feature fits in the
+                // Track rectangle draw it
+                if (pixelEnd >= trackRectangle.getX() &&
+                        pixelStart <= trackRectangle.getMaxX()) {
+
+                    // Set color used to draw the feature
+
+
+                    int width = (int) pixelEnd - (int) pixelStart;
+                    if (width < 3) {
+                        width = 3;
+                    }
+
+                    int mutHeight = (int) Math.max(1, trackRectangle.getHeight() - 2);
+                    int mutY = (int) (trackRectangle.getY() + (trackRectangle.getHeight() - mutHeight) / 2);
+
+                    Rectangle mutRect = new Rectangle((int) pixelStart, mutY, width, mutHeight);
+
+                    Color color = getColor((VCFFeature) feature);
+
+
+                    Graphics2D g = context.getGraphic2DForColor(color);
+                    g.setFont(font);
+
+                    if (getOverlayMode() == true) {
+                        g.draw(mutRect);
+                        mutRect.x--;
+                        mutRect.width += 2;
+                        g.draw(mutRect);
+
+                    } else {
+                        g.fill(mutRect);
+                        if (lastRect != null && mutRect.intersects(lastRect)) {
+                            // Indicate overlapping mutations
+                            Graphics2D g2 = context.getGraphic2DForColor(Color.BLACK);
+                            g2.draw(mutRect);
+
+                        }
+                    }
+
+                    lastRect = mutRect;
+                }
+            }
+        }
+    }
+
+}
diff --git a/src/org/broad/igv/renderer/XYPlotRenderer.java b/src/org/broad/igv/renderer/XYPlotRenderer.java
index 539379f..1df3c1a 100644
--- a/src/org/broad/igv/renderer/XYPlotRenderer.java
+++ b/src/org/broad/igv/renderer/XYPlotRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -25,13 +25,15 @@ package org.broad.igv.renderer;
 
 //~--- non-JDK imports --------------------------------------------------------
 
-import org.broad.igv.IGVConstants;
+import org.broad.igv.Globals;
 import org.broad.igv.PreferenceManager;
 import org.broad.igv.feature.LocusScore;
+import org.broad.igv.track.DataTrack;
 import org.broad.igv.track.RenderContext;
 import org.broad.igv.track.Track;
 import org.broad.igv.ui.FontManager;
 import org.broad.igv.ui.MiscStuff;
+import org.broad.igv.ui.UIConstants;
 
 import java.awt.*;
 import java.text.DecimalFormat;
@@ -42,6 +44,8 @@ import java.util.List;
  */
 public abstract class XYPlotRenderer extends DataRenderer {
 
+
+
     protected void drawDataPoint(Color graphColor, int dx, int pX, int baseY, int pY,
                                  RenderContext context) {
         context.getGraphic2DForColor(graphColor).fillRect(pX, pY, dx, 2);
@@ -56,16 +60,12 @@ public abstract class XYPlotRenderer extends DataRenderer {
      * @param context
      * @param arect
      */
-    public void renderScores(Track track, List<LocusScore> locusScores, RenderContext context,
-                             Rectangle arect) {
-
-
-        Graphics2D noDataGraphics = context.getGraphic2DForColor(IGVConstants.NO_DATA_COLOR);
+    public synchronized void  renderScores(Track track, List<LocusScore> locusScores, RenderContext context, Rectangle arect) {
 
+        Graphics2D noDataGraphics = context.getGraphic2DForColor(UIConstants.NO_DATA_COLOR);
+        Graphics2D tickGraphics = context.getGraphic2DForColor(Color.BLACK);
 
         Rectangle adjustedRect = calculateDrawingRect(arect);
-
-
         double origin = context.getOrigin();
         double locScale = context.getScale();
 
@@ -78,16 +78,31 @@ public abstract class XYPlotRenderer extends DataRenderer {
         // Get the Y axis definition, consisting of minimum, maximum, and base value.  Often
         // the base value is == min value which is == 0.
 
-        DataRange axisDefinition = track.getDataRange();
-        float maxValue = axisDefinition.getMaximum();
-        float baseValue = axisDefinition.getBaseline();
-        float minValue = axisDefinition.getMinimum();
+        DataRange dataRange = track.getDataRange();
+        float maxValue = dataRange.getMaximum();
+        float baseValue = dataRange.getBaseline();
+        float minValue = dataRange.getMinimum();
+        boolean isLog = dataRange.isLog();
+
+        if (isLog) {
+            minValue = (float) (minValue == 0 ? 0 : Math.log10(minValue));
+            maxValue = (float) Math.log10(maxValue);
+        }
+
 
         // Calculate the Y scale factor.
-        double yScaleFactor = adjustedRect.getHeight() / (maxValue - minValue);
 
-        // Calculate the Y position in pixels of the base value.
-        int baseY = (int) (adjustedRect.getY() + (maxValue - baseValue) * yScaleFactor);
+        double delta = (maxValue - minValue);
+        double yScaleFactor = adjustedRect.getHeight() / delta;
+
+        // Calculate the Y position in pixels of the base value.  Clip to bounds of rectangle
+        double baseDelta = maxValue - baseValue;
+        int baseY = (int) (adjustedRect.getY() + baseDelta * yScaleFactor);
+        if (baseY < adjustedRect.y) {
+            baseY = adjustedRect.y;
+        } else if (baseY > adjustedRect.y + adjustedRect.height) {
+            baseY = adjustedRect.y + adjustedRect.height;
+        }
 
         int lastPx = 0;
         for (LocusScore score : locusScores) {
@@ -103,18 +118,23 @@ public abstract class XYPlotRenderer extends DataRenderer {
             }
 
             float dataY = score.getScore();
+            if (isLog && dataY <= 0) {
+                continue;
+            }
 
             if (!Float.isNaN(dataY) && (score.getConfidence() > confThreshold)) {
 
+
                 // Compute the pixel y location.  Clip to bounds of rectangle.
-                int pY = baseY - (int) ((dataY - baseValue) * yScaleFactor);
+                double dy = isLog ? Math.log10(dataY) - baseValue : (dataY - baseValue);
+                int pY = baseY - (int) (dy * yScaleFactor);
                 if (pY < adjustedRect.y) {
                     pY = adjustedRect.y;
                 } else if (pY > adjustedRect.y + adjustedRect.height) {
                     pY = adjustedRect.y + adjustedRect.height;
                 }
 
-                Color color = (dataY >= 0) ? posColor : negColor;
+                Color color = (dataY >= baseValue) ? posColor : negColor;
                 drawDataPoint(color, (int) dx, (int) pX, baseY, pY, context);
 
             }
@@ -123,8 +143,7 @@ public abstract class XYPlotRenderer extends DataRenderer {
                 // Draw from lastPx + 1  to pX - 1;
                 int w = (int) pX - lastPx - 4;
                 if (w > 0) {
-                    noDataGraphics.fillRect(lastPx + 2, (int) arect.getY(), w,
-                            (int) arect.getHeight());
+                    noDataGraphics.fillRect(lastPx + 2, (int) arect.getY(), w, (int) arect.getHeight());
                 }
             }
             if (!Float.isNaN(dataY)) {
@@ -155,7 +174,7 @@ public abstract class XYPlotRenderer extends DataRenderer {
     public void renderAxis(Track track, RenderContext context, Rectangle arect) {
 
         // For now disable axes for all chromosome view
-        if (context.getChr().equals(IGVConstants.CHR_ALL)) {
+        if (context.getChr().equals(Globals.CHR_ALL)) {
             return;
         }
 
@@ -163,9 +182,7 @@ public abstract class XYPlotRenderer extends DataRenderer {
 
         Rectangle drawingRect = calculateDrawingRect(arect);
 
-
-        PreferenceManager.ChartPreferences prefs =
-                PreferenceManager.getInstance().getChartPreferences();
+        PreferenceManager.ChartPreferences prefs = PreferenceManager.getInstance().getChartPreferences();
 
         Color labelColor = prefs.isColorTrackName() ? track.getColor() : Color.black;
         Graphics2D labelGraphics = context.getGraphic2DForColor(labelColor);
@@ -178,7 +195,7 @@ public abstract class XYPlotRenderer extends DataRenderer {
             if (arect.getHeight() > 25) {
                 Rectangle labelRect = new Rectangle(arect.x, arect.y + 10, arect.width, 10);
                 labelGraphics.setFont(FontManager.getScalableFont(10));
-                GraphicUtils.drawCenteredText(track.getDisplayName(), labelRect, labelGraphics);
+                GraphicUtils.drawCenteredText(track.getName(), labelRect, labelGraphics);
             }
         }
 
@@ -196,10 +213,8 @@ public abstract class XYPlotRenderer extends DataRenderer {
             // Bottom (minimum tick mark)
             int pY = computeYPixelValue(drawingRect, axisDefinition, minValue);
 
-            labelGraphics.drawLine(axisRect.x + AXIS_AREA_WIDTH - 10, pY,
-                    axisRect.x + AXIS_AREA_WIDTH - 5, pY);
-            GraphicUtils.drawRightJustifiedText(formatter.format(minValue),
-                    axisRect.x + AXIS_AREA_WIDTH - 15, pY, labelGraphics);
+            labelGraphics.drawLine(axisRect.x + AXIS_AREA_WIDTH - 10, pY, axisRect.x + AXIS_AREA_WIDTH - 5, pY);
+            GraphicUtils.drawRightJustifiedText(formatter.format(minValue), axisRect.x + AXIS_AREA_WIDTH - 15, pY, labelGraphics);
 
             // Top (maximum tick mark)
             int topPY = computeYPixelValue(drawingRect, axisDefinition, maxValue);
@@ -223,16 +238,28 @@ public abstract class XYPlotRenderer extends DataRenderer {
                         axisRect.x + AXIS_AREA_WIDTH - 15, midPY + 4, labelGraphics);
             }
 
+        } else if (track.isShowDataRange() && arect.height > 20) {
+            DataRange range = track.getDataRange();
+            if (range != null) {
+                Graphics2D g = context.getGraphic2DForColor(Color.black);
+                Font font = g.getFont();
+                Font smallFont = FontManager.getScalableFont(8);
+                try {
+                    g.setFont(smallFont);
+                    String minString = range.getMinimum() == 0f ? "0" : String.format("%.3f", range.getMinimum());
+                    String fmtString = range.getMaximum() > 10 ? "%.0f" : "%.2f";
+                    String maxString = String.format(fmtString, range.getMaximum());
+                    String scale = "[" + minString + " - " + maxString + "]";
+                    g.drawString(scale, arect.x + 5, arect.y + 10);
+
+                } finally {
+                    g.setFont(font);
+                }
+            }
         }
     }
 
-    /**
-     * Method description
-     *
-     * @param track
-     * @param context
-     * @param arect
-     */
+
     @Override
     public void renderBorder(Track track, RenderContext context, Rectangle arect) {
 
@@ -259,20 +286,19 @@ public abstract class XYPlotRenderer extends DataRenderer {
                 getBaselineGraphics(context).drawLine((int) x, baseY, (int) maxX, baseY);
             }
 
-            PreferenceManager.ChartPreferences prefs =
-                    PreferenceManager.getInstance().getChartPreferences();
+            PreferenceManager.ChartPreferences prefs = PreferenceManager.getInstance().getChartPreferences();
 
-            Color borderColor = prefs.isColorBorders() ? track.getColor() : Color.black;
+            Color borderColor = (prefs.isColorBorders() && track.getAltColor() == track.getColor())
+                    ? track.getColor() : Color.lightGray;
             Graphics2D borderGraphics = context.getGraphic2DForColor(borderColor);
 
             // Draw the baseline -- todo, this is a wig track option?
             double zeroValue = axisDefinition.getBaseline();
             int zeroX = computeYPixelValue(adjustedRect, axisDefinition, zeroValue);
-            borderGraphics.drawLine(adjustedRect.x, zeroX, adjustedRect.x + adjustedRect.width,
-                    zeroX);
+            borderGraphics.drawLine(adjustedRect.x, zeroX, adjustedRect.x + adjustedRect.width, zeroX);
 
             // If the chart has + and - numbers draw both borders or none. This
-            // needs documented somewhere.  
+            // needs documented somewhere.
             boolean drawBorders = true;
 
             if (minValue * maxValue < 0) {
@@ -301,23 +327,7 @@ public abstract class XYPlotRenderer extends DataRenderer {
      */
     private static Graphics2D getBaselineGraphics(RenderContext context) {
         Graphics2D baselineGraphics;
-        Stroke thindashed = new BasicStroke(1.0f,    // line width
-
-                /* cap style */
-                BasicStroke.CAP_BUTT,
-
-                /* join style, miter limit */
-                BasicStroke.JOIN_BEVEL, 1.0f,
-
-                /* the dash pattern */
-                new float[]{8.0f, 3.0f, 2.0f, 3.0f},
-
-                /* the dash phase */
-                0.0f);    /* on 8, off 3, on 2, off 3 */
-
         baselineGraphics = (Graphics2D) context.getGraphic2DForColor(Color.lightGray).create();
-
-        // baselineGraphics.setStroke(thindashed);
         return baselineGraphics;
     }
 
@@ -355,4 +365,5 @@ public abstract class XYPlotRenderer extends DataRenderer {
 
         return adjustedRect;
     }
+
 }
diff --git a/src/org/broad/igv/repeats/RepeatMaskSplitter.java b/src/org/broad/igv/repeats/RepeatMaskSplitter.java
index a0a94ca..f6b14b1 100644
--- a/src/org/broad/igv/repeats/RepeatMaskSplitter.java
+++ b/src/org/broad/igv/repeats/RepeatMaskSplitter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -17,8 +17,8 @@
  */
 package org.broad.igv.repeats;
 
-import org.broad.igv.util.ParsingUtils;
 import org.broad.igv.util.AsciiLineReader;
+import org.broad.igv.util.ParsingUtils;
 
 import java.io.*;
 import java.util.HashMap;
diff --git a/src/org/broad/igv/roc/ROC.java b/src/org/broad/igv/roc/ROC.java
deleted file mode 100644
index cc220cf..0000000
--- a/src/org/broad/igv/roc/ROC.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-
-package org.broad.igv.roc;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import cern.colt.GenericSorting;
-
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.IOException;
-
-
-/**
- * Computes the ROC AUC (area under the curve) given a classification vector and a set of scores.
- * Reference:
- * <p/>
- * ROC Graphs: Notes and Practical Considerations for Data Mining Researchers
- * Tom Fawcett
- * Intelligent Enterprise Technologies Laboratory
- * HP Laboratories Palo Alto
- * HPL-2003-4
- * January 7th , 2003* *
- *
- * @author jrobinso
- */
-public class ROC {
-
-    public static boolean ENABLED = false;
-
-    private int nPts;
-    private int[] classVector;
-    private float[] values;
-
-    /**
-     * Constructs ...
-     *
-     * @param classVector
-     * @param values
-     */
-    public ROC(int[] classVector, float[] values) {
-        this.classVector = classVector;
-        this.values = values;
-        nPts = Math.min(classVector.length, values.length);
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public double computeAUC() {
-
-        GenericSorting.quickSort(0, classVector.length, new ROCComparator(), new ROCSwapper());
-
-        int totalPositives = 0;
-        int totalNegatives = 0;
-        double nTruePositives = 0;
-        double nFalsePositives = 0;
-        for (int i = 0; i < nPts; i++) {
-            if (classVector[i] == 0) {
-                totalNegatives++;
-            } else {
-                totalPositives++;
-            }
-        }
-
-        double area = 0;
-        double lastFpRate = 0;
-        double lastTpRate = 0;
-        for (int i = 0; i < nPts; i++) {
-            if (classVector[i] == 1) {
-                nTruePositives++;
-            } else {
-                nFalsePositives++;
-            }
-            double fpRate = nFalsePositives / totalNegatives;
-
-            if (fpRate != lastFpRate) {
-                double tpRate = nTruePositives / totalPositives;
-                double width = fpRate - lastFpRate;
-                area += width * tpRate;
-                lastFpRate = fpRate;
-                lastTpRate = tpRate;
-            }
-        }
-
-        // Last rectangle
-        if (lastFpRate < 1) {
-            double width = 1 - lastFpRate;
-            area += width * lastTpRate;
-        }
-
-        return area;
-    }
-
-    class ROCSwapper implements cern.colt.Swapper {
-
-
-        /**
-         * Method description
-         *
-         * @param a
-         * @param b
-         */
-        public void swap(int a, int b) {
-
-            int temp;
-            temp = classVector[a];
-            classVector[a] = classVector[b];
-            classVector[b] = temp;
-
-            float t = values[a];
-            values[a] = values[b];
-            values[b] = t;
-
-        }
-
-
-    }
-
-
-    class ROCComparator implements cern.colt.function.IntComparator {
-
-        /**
-         * Method description
-         *
-         * @param i
-         * @param j
-         * @return
-         */
-        public int compare(int i, int j) {
-
-            if (values[i] < values[j]) {
-                return 1;
-            } else {
-                if (values[i] > values[j]) {
-                    return -1;
-
-                } else {
-                    return 0;
-                }
-            }
-        }
-    }
-
-
-    /**
-     * Method description
-     * TODO -- turn this into a unit test
-     *
-     * @param args
-     * @throws IOException
-     */
-    public static void main(String[] args) throws IOException {
-
-
-        BufferedReader br = new BufferedReader(new FileReader("test.txt"));
-
-        // Count lines
-        int nLines = 0;
-        String nextLine;
-        while ((nextLine = br.readLine()) != null) {
-            nLines++;
-        }
-        br.close();
-
-        int[] classVector = new int[nLines];
-        float[] values = new float[nLines];
-
-        int n = 0;
-        br = new BufferedReader(new FileReader("test.txt"));
-        while ((nextLine = br.readLine()) != null) {
-            String[] tokens = nextLine.split("\t");
-            values[n] = Float.parseFloat(tokens[0]);
-            classVector[n] = Integer.parseInt(tokens[1]);
-            n++;
-        }
-        br.close();
-
-
-        ROC roc = new ROC(classVector, values);
-        System.out.println("AUC = " + roc.computeAUC());
-
-
-    }
-
-}
diff --git a/src/org/broad/igv/roc/ROCScore.java b/src/org/broad/igv/roc/ROCScore.java
deleted file mode 100644
index 4b650a1..0000000
--- a/src/org/broad/igv/roc/ROCScore.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-
-package org.broad.igv.roc;
-
-
-/**
- * @author jrobinso
- */
-public class ROCScore implements Comparable<ROCScore> {
-
-    private double auc;
-    private String gene;
-
-    /**
-     * Constructs ...
-     *
-     * @param auc
-     * @param gene
-     */
-    public ROCScore(double auc, String gene) {
-        this.auc = auc;
-        this.gene = gene;
-    }
-
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public double getAUC() {
-        return auc;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public String getGene() {
-        return gene;
-    }
-
-    /**
-     * Method description
-     *
-     * @param score
-     * @return
-     */
-    public int compareTo(ROCScore score) {
-        return (int) (this.auc - score.auc);
-    }
-
-
-}
diff --git a/src/org/broad/igv/roc/ROCUtils.java b/src/org/broad/igv/roc/ROCUtils.java
deleted file mode 100644
index bbc3c86..0000000
--- a/src/org/broad/igv/roc/ROCUtils.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-package org.broad.igv.roc;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import hep.aida.bin.StaticBin1D;
-import org.broad.igv.feature.Feature;
-import org.broad.igv.feature.GeneManager;
-import org.broad.igv.track.RegionScoreType;
-import org.broad.igv.track.Track;
-import org.broad.igv.track.TrackManager;
-import org.broad.igv.track.TrackType;
-
-import java.util.*;
-
-
-/**
- * @author jrobinso
- */
-public class ROCUtils {
-
-    static int MAX_GENES = 5000;
-
-    String attribute;
-    String chr;
-    int start;
-    int end;
-    int zoom;
-
-    List<String> samples;
-
-    Map<String, List<Track>> expressionTracks = new HashMap();
-
-
-    /**
-     * Constructs ...
-     *
-     * @param attribute
-     * @param chr
-     * @param start
-     * @param end
-     * @param zoom
-     */
-    public ROCUtils(String attribute, String chr, int start, int end, int zoom) {
-
-        {
-            this.attribute = attribute;
-            this.chr = chr;
-            this.start = start;
-            this.end = end;
-            this.zoom = zoom;
-        }
-    }
-
-
-    /**
-     * Method description
-     *
-     * @param threshold
-     * @return
-     */
-    public List<ROCScore> computeScores(float threshold) {
-
-        List<ROCScore> scores = new ArrayList(25000);
-
-        int[] classVector = initCopyNumberMap(threshold);
-
-        Collection<String> chromosomes = GeneManager.getGeneManager("hg18").getChromosomes();
-        for (String c : chromosomes) {
-            if (scores.size() > MAX_GENES) {
-                break;
-            }
-            for (Feature gene : GeneManager.getGeneManager("hg18").getGenesForChromosome(c)) {
-
-                // Feature gene = GeneManager.getGeneManager("hg18").getGene("AASDH");
-                int[] cv = new int[classVector.length];
-                System.arraycopy(classVector, 0, cv, 0, classVector.length);
-                int start = gene.getStart() - 10;
-                int end = gene.getEnd() + 10;
-                float[] values = computeExpressionValues(gene.getChromosome(), start, end);
-                ROC roc = new ROC(cv, values);
-                double auc = roc.computeAUC();
-                scores.add(new ROCScore(auc, gene.getName()));
-
-                if (scores.size() > MAX_GENES) {
-                    break;
-                }
-            }
-        }
-
-        Collections.sort(scores);
-
-        return scores;
-    }
-
-
-    // Find copyNumbers that have both cn and expression tracks
-    int[] initCopyNumberMap(float threshold) {
-
-        Map<String, StaticBin1D> copyNumbers = new HashMap();
-        samples = new ArrayList();
-
-        // Collect set of sample names with at least one gene expression track
-        for (Track track : TrackManager.getInstance().getAllTracks(false)) {
-            if ((track.getTrackType() == TrackType.GENE_EXPRESSION) && track.isVisible()) {
-                String sample = track.getAttributeValue(attribute);
-                List<Track> eTracks = expressionTracks.get(sample);
-                if (eTracks == null) {
-                    eTracks = new ArrayList();
-                    expressionTracks.put(sample, eTracks);
-                }
-                eTracks.add(track);
-            }
-
-        }
-
-        // Initial value bins, one for each sample that (1) is visible, and (2) contains
-        // both copy # and expression values.
-        for (Track track : TrackManager.getInstance().getAllTracks(false)) {
-            if ((track.getTrackType() == TrackType.COPY_NUMBER) && track.isVisible()) {
-                String sample = track.getAttributeValue(attribute);
-                if (expressionTracks.containsKey(sample)) {
-                    StaticBin1D bin = copyNumbers.get(sample);
-                    if (bin == null) {
-                        bin = new StaticBin1D();
-                        copyNumbers.put(sample, bin);
-                        samples.add(sample);
-                    }
-                    bin.add(track.getRegionScore(chr, start, end, zoom,
-                            RegionScoreType.AMPLIFICATION));
-                }
-            }
-
-        }
-
-
-        int[] classVector = new int[samples.size()];
-        int n = 0;
-        for (String sample : samples) {
-            double cn = copyNumbers.get(sample).max();
-            classVector[n] = (cn > threshold) ? 1 : 0;
-            n++;
-        }
-
-
-        return classVector;
-    }
-
-    /**
-     * @return
-     */
-    float[] computeExpressionValues(String chr, int start, int end) {
-
-        Map<String, Float> expressionValues = new HashMap(samples.size() * 2);
-
-        for (String sample : samples) {
-            for (Track track : expressionTracks.get(sample)) {
-
-                Float val = track.getRegionScore(chr, start, end, zoom, RegionScoreType.EXPRESSION);
-                if (expressionValues.containsKey(sample)) {
-                    expressionValues.put(sample, Math.max(val, expressionValues.get(sample)));
-                } else {
-                    expressionValues.put(sample, val);
-                }
-
-            }
-        }
-
-        int n = 0;
-        float[] values = new float[samples.size()];
-        for (String sample : samples) {
-            values[n] = expressionValues.get(sample).floatValue();
-            n++;
-        }
-
-
-        return values;
-    }
-
-
-}
diff --git a/src/org/broad/igv/sam/AbstractAlignment.java b/src/org/broad/igv/sam/AbstractAlignment.java
index ad36440..78f9480 100644
--- a/src/org/broad/igv/sam/AbstractAlignment.java
+++ b/src/org/broad/igv/sam/AbstractAlignment.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,8 +22,11 @@
  */
 package org.broad.igv.sam;
 
+import org.broad.igv.feature.Strand;
 import org.broad.igv.track.WindowFunction;
 
+import java.awt.*;
+
 /**
  * @author jrobinso
  */
@@ -53,7 +56,12 @@ public abstract class AbstractAlignment implements Alignment {
         this.insertions = alignment.insertions;
     }
 
+
     public String getChromosome() {
+        return getChr();
+    }
+
+    public String getChr() {
         return chr;
     }
 
@@ -65,6 +73,10 @@ public abstract class AbstractAlignment implements Alignment {
         return mate;
     }
 
+    public String getMateSequence() {
+        return null;
+    }
+
     public String getReadName() {
         return readName;
     }
@@ -160,8 +172,8 @@ public abstract class AbstractAlignment implements Alignment {
         buf.append("----------------------" + "<br>");
 
         for (AlignmentBlock block : this.alignmentBlocks) {
-            if (block.contains((int) basePosition)) {
-                int offset = (int) basePosition - block.getStart();
+            if (block.contains(basePosition)) {
+                int offset = basePosition - block.getStart();
                 byte base = block.getBases()[offset];
                 byte quality = block.getQuality(offset);
                 buf.append("Base = " + (char) base + "<br>");
@@ -174,9 +186,12 @@ public abstract class AbstractAlignment implements Alignment {
             buf.append("Pair start = " + getMate().positionString() + "<br>");
             buf.append("Pair is mapped = " + (getMate().isMapped() ? "yes" : "no") + "<br>");
             //buf.append("Pair is proper = " + (getProperPairFlag() ? "yes" : "no") + "<br>");
-            if (this.getChromosome().equals(getMate().mateChr)) {
+            if (getChr().equals(getMate().mateChr)) {
                 buf.append("Insert size = " + getInferredInsertSize() + "<br>");
             }
+            if (getPairOrientation().length() > 0) {
+                buf.append("Pair orientation = " + getPairOrientation() + "<br>");
+            }
         }
         buf.append("----------------------");
         return buf.toString();
@@ -190,6 +205,11 @@ public abstract class AbstractAlignment implements Alignment {
 
     public abstract boolean isProperPair();
 
+    public boolean isSmallInsert() {
+        int absISize = Math.abs(getInferredInsertSize());
+        return absISize > 0 && absISize <= getReadSequence().length();
+    }
+
     public float getConfidence() {
         return ((float) getMappingQuality()) / 255;
     }
@@ -219,8 +239,7 @@ public abstract class AbstractAlignment implements Alignment {
     /**
      * @param mate the mate to set
      */
-    public void setMate( // 255 by default
-                         ReadMate mate) {
+    public void setMate(ReadMate mate) {
         this.mate = mate;
     }
 
@@ -232,5 +251,29 @@ public abstract class AbstractAlignment implements Alignment {
         return null;
     }
 
+    public Object getAttribute(String key) {
+        return null;
+    }
+
+    public Strand getFragmentStrand() {
+        return Strand.NONE;
+    }
+
+    public void setMateSequence(String sequence) {
+        // ignore by default
+    }
+
+    public String getPairOrientation() {
+        return "";
+    }
+
+    public boolean isVendorFailedRead() {
+        return false;
+    }
+
+
+    public Color getDefaultColor() {
+        return AlignmentRenderer.grey1;
+    }
 
 }
diff --git a/src/org/broad/igv/sam/AbstractRenderer.java b/src/org/broad/igv/sam/AbstractRenderer.java
deleted file mode 100644
index 3781012..0000000
--- a/src/org/broad/igv/sam/AbstractRenderer.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-package org.broad.igv.sam;
-
-import java.awt.*;
-
-/**
- * @author jrobinso
- */
-public class AbstractRenderer {
-
-
-    void drawCenteredText(Graphics2D g, char[] chars, int x, int y, int w, int h) {
-        // Get measures needed to center the message
-        FontMetrics fm = g.getFontMetrics();
-        // How many pixels wide is the string
-        int msg_width = fm.charsWidth(chars, 0, 1);
-        // How far above the baseline can the font go?
-        int ascent = fm.getMaxAscent();
-        // How far below the baseline?
-        int descent = fm.getMaxDescent();
-        // Use the string width to find the starting point
-        int msgX = x + w / 2 - msg_width / 2;
-        // Use the vertical height of this font to find
-        // the vertical starting coordinate
-        int msgY = y + h / 2 - descent / 2 + ascent / 2;
-        g.drawChars(chars, 0, 1, msgX, msgY);
-    }
-
-}
diff --git a/src/org/broad/igv/sam/Alignment.java b/src/org/broad/igv/sam/Alignment.java
index 8848451..c9745cd 100644
--- a/src/org/broad/igv/sam/Alignment.java
+++ b/src/org/broad/igv/sam/Alignment.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,6 +23,9 @@
 package org.broad.igv.sam;
 
 import org.broad.igv.feature.LocusScore;
+import org.broad.igv.feature.Strand;
+
+import java.awt.*;
 
 /**
  * @author jrobinso
@@ -33,8 +36,11 @@ public interface Alignment extends LocusScore {
 
     String getReadSequence();
 
+    // For legacy apps
     public String getChromosome();
 
+    public String getChr();
+
     int getAlignmentStart();
 
     boolean contains(double location);
@@ -75,4 +81,19 @@ public interface Alignment extends LocusScore {
     String getSample();
 
     String getReadGroup();
+
+    Object getAttribute(String key);
+
+    public Strand getFragmentStrand(int read);
+
+    public void setMateSequence(String sequence);
+
+    String getPairOrientation();
+
+    boolean isSmallInsert();
+
+    boolean isVendorFailedRead();
+
+    Color getDefaultColor();
+
 }
diff --git a/src/org/broad/igv/sam/AlignmentBlock.java b/src/org/broad/igv/sam/AlignmentBlock.java
index c455200..164841d 100644
--- a/src/org/broad/igv/sam/AlignmentBlock.java
+++ b/src/org/broad/igv/sam/AlignmentBlock.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -27,6 +27,7 @@ public class AlignmentBlock {
     private int start;
     private byte[] bases;
     private byte[] qualities;
+    private boolean softClipped = false;
 
     public AlignmentBlock(int start, byte[] bases, byte[] qualities) {
         this.start = start;
@@ -48,14 +49,26 @@ public class AlignmentBlock {
     }
 
     public byte getQuality(int offset) {
-        return   qualities.length > offset ? qualities[offset] : (byte) 255;
+        return   qualities.length > offset ? qualities[offset] : (byte) 126;
 
     }
 
+    public byte[] getQualities() {
+        return qualities;
+    }
+
     /**
      * Convenience method
      */
     public int getEnd() {
         return start + bases.length;
     }
+
+    public boolean isSoftClipped() {
+        return softClipped;
+    }
+
+    public void setSoftClipped(boolean softClipped) {
+        this.softClipped = softClipped;
+    }
 }
diff --git a/src/org/broad/igv/sam/AlignmentDataManager.java b/src/org/broad/igv/sam/AlignmentDataManager.java
index 484ce7a..3476b70 100644
--- a/src/org/broad/igv/sam/AlignmentDataManager.java
+++ b/src/org/broad/igv/sam/AlignmentDataManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -19,16 +19,18 @@ package org.broad.igv.sam;
 
 import net.sf.samtools.util.CloseableIterator;
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
+import org.broad.igv.Globals;
 import org.broad.igv.PreferenceManager;
+import org.broad.igv.feature.Genome;
 import org.broad.igv.sam.AlignmentTrack.SortOption;
-import org.broad.igv.sam.reader.AlignmentQueryReader;
+import org.broad.igv.sam.reader.CachingQueryReader;
 import org.broad.igv.sam.reader.SamListReader;
 import org.broad.igv.sam.reader.SamQueryReaderFactory;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.track.MultiFileWrapper;
 import org.broad.igv.ui.IGVMainFrame;
-import org.broad.igv.ui.IGVModel;
-import org.broad.igv.ui.LongRunningTask;
+import org.broad.igv.util.LongRunningTask;
+import org.broad.igv.util.NamedRunnable;
 import org.broad.igv.util.ResourceLocator;
 
 import javax.swing.*;
@@ -39,47 +41,59 @@ public class AlignmentDataManager {
 
     private static Logger log = Logger.getLogger(AlignmentDataManager.class);
     private AlignmentInterval loadedInterval = null;
-    private List<Row> alignmentRows;
-    boolean ensembleChrConventions;
-    boolean isLoading = false;
-    AlignmentQueryReader reader;
+    private HashMap<String, String> chrMappings = new HashMap();
+    private boolean isLoading = false;
+    private CachingQueryReader reader;
+    private CoverageTrack coverageTrack;
+    private int maxLevels;
 
-    CoverageTrack coverageTrack;
-
-    static Hashtable<ResourceLocator, AlignmentDataManager> managers = new Hashtable();
     private static final int DEFAULT_DEPTH = 10;
 
 
-    public synchronized static AlignmentDataManager getDataManager(ResourceLocator locator) {
-        AlignmentDataManager mgr = managers.get(locator);
-        if (mgr == null) {
-            mgr = new AlignmentDataManager(locator);
-            managers.put(locator, mgr);
-        }
-        return mgr;
+    public static AlignmentDataManager getDataManager(ResourceLocator locator) throws IOException {
+        return new AlignmentDataManager(locator);
     }
 
-    public AlignmentQueryReader getReader() {
-        return reader;
-    }
 
-    private AlignmentDataManager(ResourceLocator locator) {
+    public AlignmentDataManager(ResourceLocator locator) throws IOException {
 
-        if (locator.getPath().endsWith(".list")) {
+        PreferenceManager.SAMPreferences prefs = PreferenceManager.getInstance().getSAMPreferences();
+        maxLevels = prefs.getMaxLevels();
+
+        if (locator.getPath().endsWith(".sam.list")) {
             MultiFileWrapper mfw = MultiFileWrapper.parse(locator);
-            reader = new SamListReader(mfw.getLocators());
+            reader = new CachingQueryReader(new SamListReader(mfw.getLocators()));
         } else {
-            reader = SamQueryReaderFactory.getReader(locator);
+            reader = new CachingQueryReader(SamQueryReaderFactory.getReader(locator));
         }
+        initChrMap();
     }
 
+    private void initChrMap() {
+        Genome genome = ViewContext.getInstance().getGenome();
+        if (genome != null) {
+            Set<String> seqNames = reader.getSequenceNames();
+            if (seqNames != null) {
+                for (String chr : seqNames) {
+                    String alias = genome.getChromosomeAlias(chr);
+                    chrMappings.put(alias, chr);
+                }
+            }
+        }
+    }
+
+    public CachingQueryReader getReader() {
+        return reader;
+    }
+
+
     public boolean hasIndex() {
         return reader.hasIndex();
     }
 
     public int getMaxDepth() {
         return loadedInterval == null ? DEFAULT_DEPTH :
-                (loadedInterval.getMaxCount() == 0 ? DEFAULT_DEPTH : loadedInterval.getMaxCount());
+                (loadedInterval.getDepth() == 0 ? DEFAULT_DEPTH : loadedInterval.getDepth());
     }
 
     public void setCoverageTrack(CoverageTrack coverageTrack) {
@@ -90,127 +104,109 @@ public class AlignmentDataManager {
         return loadedInterval;
     }
 
-    public AlignmentInterval getInterval(String chr, int start, int end) {
-
-        if (loadedInterval != null && loadedInterval.contains(chr, start, end)) {
-            return loadedInterval;
-        } else {
-            return null;
-        }
-    }
-
-    public void setEnsembleChrConventions(boolean ensembleChrConventions) {
-        this.ensembleChrConventions = ensembleChrConventions;
-    }
 
     /**
      * Sort alignment rows such that alignments that intersect from the
      * center appear left to right by start position
      */
     public void sortRows(SortOption option) {
-        if (getAlignmentRows() == null) {
-            return;
+        if (loadedInterval != null) {
+            loadedInterval.sortRows(option);
         }
+    }
 
-        double center = IGVModel.getInstance().getViewContext().getCenter();
-        for (Row row : getAlignmentRows()) {
-            if (option == SortOption.NUCELOTIDE) {
-
-            }
-            row.updateScore(option, center, loadedInterval);
+    public void sortRows(SortOption option, double location) {
+        if (loadedInterval != null) {
+            loadedInterval.sortRows(option, location);
         }
-
-        Collections.sort(getAlignmentRows(), new Comparator<Row>() {
-
-            public int compare(Row arg0, Row arg1) {
-                if (arg0.getScore() > arg1.getScore()) {
-                    return 1;
-                } else if (arg0.getScore() > arg1.getScore()) {
-                    return -1;
-                }
-                return 0;
-            }
-        });
     }
 
     public void packAlignments() {
+        if (loadedInterval == null) {
+            return;
+        }
         RowIterator iter = new RowIterator();
-        final PreferenceManager.SAMPreferences prefs = PreferenceManager.getInstance().getSAMPreferences();
-        final int maxLevels = prefs.getMaxLevels();
-        final int qualityThreshold = prefs.getQualityThreshold();
 
-        alignmentRows = AlignmentPacker.packAlignments(
+        List<AlignmentInterval.Row> alignmentRows = AlignmentLoader.packAlignments(
                 iter,
-                prefs.isShowDuplicates(),
-                qualityThreshold,
                 maxLevels,
                 getLoadedInterval().getEnd());
+        loadedInterval.setAlignmentRows(alignmentRows);
     }
 
 
-    public synchronized void preloadData(final String chr, final int start, final int end, int zoom) {
+    public synchronized List<AlignmentInterval.Row> getAlignmentRows(String genomeId, final String chr, final int start, final int end) {
+        log.debug("Enter getAlignmentRows: " + chr + ":" + start + "-" + end);
+
+        // If we've moved out of the loaded interval start a new load.
+        if (loadedInterval == null || !loadedInterval.contains(genomeId, chr, start, end)) {
+            loadAlignments(genomeId, chr, start, end);
+        }
+
+        // If there is any overlap in the loaded interval and the requested interval return it.
+        if (loadedInterval != null && loadedInterval.overlaps(genomeId, chr, start, end)) {
+            return loadedInterval.getAlignmentRows();
+        } else {
+            return null;
+        }
+    }
+
+    public void reload() {
+        reader.clearCache();
+        if (loadedInterval != null) {
+            String genomeId = loadedInterval.genomeId;
+            String chr = loadedInterval.getChr();
+            int start = loadedInterval.getStart();
+            int end = loadedInterval.getEnd();
+            loadedInterval = null;
+            loadAlignments(genomeId, chr, start, end);
+        }
+    }
 
-        final PreferenceManager.SAMPreferences prefs = PreferenceManager.getInstance().getSAMPreferences();
-        final float maxRange = prefs.getMaxVisibleRange();
-        final int maxLevels = prefs.getMaxLevels();
-        final int qualityThreshold = prefs.getQualityThreshold();
-        float minVisibleScale = (maxRange * 1000) / 700;
+    public void loadAlignments(final String genomeId, final String chr, final int start, final int end) {
 
-        if (IGVModel.getInstance().getViewContext().getScale() > minVisibleScale ||
-                chr.equals(IGVConstants.CHR_ALL)) {
+        if (isLoading || chr.equals(Globals.CHR_ALL)) {
             return;
         }
 
         // If the requested interval is outside the currently loaded range load
-        final String genomeId = IGVModel.getInstance().getViewContext().getGenomeId();
-        if (getLoadedInterval() == null || !loadedInterval.contains(genomeId, chr, start, end) && !isLoading) {
+        if (loadedInterval == null || !loadedInterval.contains(genomeId, chr, start, end)) {
+            log.debug("Load alignments.  isLoading=" + isLoading);
             isLoading = true;
+            NamedRunnable runnable = new NamedRunnable() {
 
-            Runnable runnable = new Runnable() {
+                public String getName() {
+                    return "preloadData";
+                }
 
                 public void run() {
 
-                    log.debug("Preload data");
-
-                    alignmentRows = new ArrayList(maxLevels);
+                    log.debug("Loading " + chr + ":" + start + "-" + end);
+                    final PreferenceManager.SAMPreferences prefs = PreferenceManager.getInstance().getSAMPreferences();
 
                     // Expand start and end to facilitate panning, but by no more than
                     // 1 screen or 8kb, whichever is less
                     // DON'T expand mitochondria
 
-                    int expandLength = isMitochondria(chr) ? 0 :
-                            Math.min(8000, end - start) / 2;
-
-
+                    int expandLength = reader.getTileSize(chr) / 2;                    
                     int intervalStart = Math.max(0, start - expandLength);
                     int intervalEnd = end + expandLength;
                     CloseableIterator<Alignment> iter = null;
                     try {
 
-                        String sequence = chr;
-                        // TODO -- put here to handle 1 vs chr1 problems.  Fix this fragile test for human genome
-                        if (ensembleChrConventions &&
-                                (genomeId.startsWith("hg") || genomeId.startsWith("mm") ||
-                                        genomeId.equalsIgnoreCase("1kg_ref"))) {
-                            sequence = sequence.replace("chrM", "MT");
-                            sequence = chr.replace("chr", "");
-                        }
-                        iter = reader.query(sequence, intervalStart, intervalEnd, false);
+                        String sequence = chrMappings.containsKey(chr) ? chrMappings.get(chr) : chr;
+
+                        List<CachingQueryReader.AlignmentCounts> counts = new ArrayList();
 
-                        alignmentRows = AlignmentPacker.packAlignments(iter,
-                                prefs.isShowDuplicates(),
-                                qualityThreshold,
+                        iter = reader.query(sequence, intervalStart, intervalEnd, counts);
+
+                        List<AlignmentInterval.Row> alignmentRows = AlignmentLoader.packAlignments(iter,
                                 maxLevels,
                                 intervalEnd);
 
-                        setLoadedInterval(new AlignmentInterval(genomeId, chr, intervalStart, intervalEnd));
+                        loadedInterval = new AlignmentInterval(genomeId, chr, intervalStart, intervalEnd,
+                                alignmentRows, counts);
 
-                        // Caclulate coverage.  TODO -- refactor/move this
-                        for (AlignmentDataManager.Row row : getAlignmentRows()) {
-                            for (Alignment a : row.alignments) {
-                                getLoadedInterval().incCounts(a);
-                            }
-                        }
 
                         if (coverageTrack != null) {
                             coverageTrack.rescale();
@@ -219,6 +215,7 @@ public class AlignmentDataManager {
                         IGVMainFrame.getInstance().doResizeTrackPanels();
                         IGVMainFrame.getInstance().repaintDataAndHeaderPanels();
 
+
                     } catch (Exception exception) {
                         log.error("Error loading alignments", exception);
                         JOptionPane.showMessageDialog(IGVMainFrame.getInstance(), "Error reading file: " + exception.getMessage());
@@ -231,22 +228,13 @@ public class AlignmentDataManager {
                 }
             };
 
-            // Don't block the swing dispatch thread
-            if (SwingUtilities.isEventDispatchThread()) {
-                try {
-
-                    LongRunningTask.submit(runnable).get();   // <= This will force wait until the task is complete
-                } catch (Exception ex) {
-                    log.error("Error loading alignments", ex);
-                }
-            } else {
-                runnable.run();
-            }
-
+            LongRunningTask.submit(runnable);
 
         }
+
     }
 
+
     private boolean isMitochondria(String chr) {
         return chr.equals("M") || chr.equals("chrM") ||
                 chr.equals("MT") || chr.equals("chrMT");
@@ -255,19 +243,10 @@ public class AlignmentDataManager {
     /**
      * @return the alignmentRows
      */
-    public List<Row> getAlignmentRows() {
-        return alignmentRows;
+    public List<AlignmentInterval.Row> getAlignmentRows() {
+        return loadedInterval == null ? null : loadedInterval.getAlignmentRows();
     }
 
-
-    /**
-     * @param loadedInterval the loadedInterval to set
-     */
-    public void setLoadedInterval(AlignmentInterval loadedInterval) {
-        this.loadedInterval = loadedInterval;
-    }
-
-
     @Override
     protected void finalize() throws Throwable {
         super.finalize();
@@ -281,124 +260,16 @@ public class AlignmentDataManager {
 
     }
 
-    public static class Row {
-        int nextIdx;
-        private double score = 0;
-        List<Alignment> alignments;
-        private int start;
-        private int lastEnd;
-
-        public Row() {
-            nextIdx = 0;
-            this.alignments = new ArrayList(100);
-        }
-
-        public void addAlignment(Alignment alignment) {
-            if (alignments.isEmpty()) {
-                this.start = alignment.getStart();
-            }
-            alignments.add(alignment);
-            lastEnd = alignment.getEnd();
-
-            //System.out.println("Row: " + idx + " Added alignment: \t" + alignment.getAlignmentStart() +
-            //        "\t" + alignment.getEnd());
-        }
-
-        public void updateScore(SortOption option, double center, AlignmentInterval loadedInterval) {
-
-            int adjustedCenter = (int) center;
-            Alignment centerAlignment = getFeatureContaining(alignments, adjustedCenter);
-            if (centerAlignment == null) {
-                setScore(Double.MAX_VALUE);
-            } else {
-                switch (option) {
-                    case START:
-                        setScore(centerAlignment.getStart());
-                        break;
-                    case STRAND:
-                        setScore(centerAlignment.isNegativeStrand() ? -1 : 1);
-                        break;
-                    case NUCELOTIDE:
-                        byte base = centerAlignment.getBase(adjustedCenter);
-                        byte ref = loadedInterval.getReference(adjustedCenter);
-                        if (base == 'N' || base == 'n') {
-                            setScore(Integer.MAX_VALUE - 1);
-                        } else if (base == ref) {
-                            setScore(Integer.MAX_VALUE);
-                        } else {
-                            int count = loadedInterval.getCount(adjustedCenter, base);
-                            byte phred = centerAlignment.getPhred(adjustedCenter);
-                            float score = -(count + (phred / 100.0f));
-                            setScore(score);
-                        }
-                        break;
-                    case QUALITY:
-                        setScore(-centerAlignment.getMappingQuality());
-                        break;
-                    case SAMPLE:
-                        String sample = centerAlignment.getSample();
-                        score = sample == null ? 0 : sample.hashCode();
-                        setScore(score);
-                        break;
-                    case READ_GROUP:
-                        String readGroup = centerAlignment.getReadGroup();
-                        score = readGroup == null ? 0 : readGroup.hashCode();
-                        setScore(score);
-                        break;
-                }
-            }
-        }
-
-        // Used for iterating over all alignments, e.g. for packing
-        public Alignment nextAlignment() {
-            if (nextIdx < alignments.size()) {
-                Alignment tmp = alignments.get(nextIdx);
-                nextIdx++;
-                return tmp;
-            } else {
-                return null;
-            }
-        }
-
-        public int getNextStartPos() {
-            if (nextIdx < alignments.size()) {
-                return alignments.get(nextIdx).getAlignmentStart();
-            } else {
-                return Integer.MAX_VALUE;
-            }
-        }
-
-        public boolean hasNext() {
-            return nextIdx < alignments.size();
-        }
-
-        public void resetIdx() {
-            nextIdx = 0;
-        }
-
-        /**
-         * @return the score
-         */
-        public double getScore() {
-            return score;
-        }
-
-        /**
-         * @param score the score to set
-         */
-        public void setScore(double score) {
-            this.score = score;
-        }
-
-        public int getStart() {
-            return start;
-        }
+    public int getMaxLevels() {
+        return maxLevels;
+    }
 
-        public int getLastEnd() {
-            return lastEnd;
-        }
+    public void setMaxLevels(int maxLevels) {
+        reader.clearCache();
+        this.maxLevels = maxLevels;
     }
 
+
     /**
      * An alignment iterator that iterates over packed rows.  Used for
      * "repacking".   Using the iterator avoids the need to copy alignments
@@ -406,18 +277,18 @@ public class AlignmentDataManager {
      */
     class RowIterator implements CloseableIterator<Alignment> {
 
-        PriorityQueue<Row> rows;
+        PriorityQueue<AlignmentInterval.Row> rows;
         Alignment nextAlignment;
 
         RowIterator() {
-            rows = new PriorityQueue(5, new Comparator<Row>() {
+            rows = new PriorityQueue(5, new Comparator<AlignmentInterval.Row>() {
 
-                public int compare(Row o1, Row o2) {
+                public int compare(AlignmentInterval.Row o1, AlignmentInterval.Row o2) {
                     return o1.getNextStartPos() - o2.getNextStartPos();
                 }
             });
 
-            for (Row r : getAlignmentRows()) {
+            for (AlignmentInterval.Row r : loadedInterval.getAlignmentRows()) {
                 r.resetIdx();
                 rows.add(r);
             }
@@ -444,7 +315,7 @@ public class AlignmentDataManager {
         private void advance() {
 
             nextAlignment = null;
-            Row nextRow = null;
+            AlignmentInterval.Row nextRow = null;
             while (nextAlignment == null && !rows.isEmpty()) {
                 while ((nextRow = rows.poll()) != null) {
                     if (nextRow.hasNext()) {
@@ -463,41 +334,5 @@ public class AlignmentDataManager {
         }
     }
 
-    private static Alignment getFeatureContaining(
-            List<Alignment> features, int right) {
-
-        int leftBounds = 0;
-        int rightBounds = features.size() - 1;
-        int idx = features.size() / 2;
-        int lastIdx = -1;
-
-        while (idx != lastIdx) {
-            lastIdx = idx;
-            Alignment f = features.get(idx);
-            if (f.contains(right)) {
-                return f;
-            }
-
-            if (f.getStart() > right) {
-                rightBounds = idx;
-                idx = (leftBounds + idx) / 2;
-            } else {
-                leftBounds = idx;
-                idx = (rightBounds + idx) / 2;
-
-            }
-
-        }
-        // Check the extremes
-        if (features.get(0).contains(right)) {
-            return features.get(0);
-        }
-
-        if (features.get(rightBounds).contains(right)) {
-            return features.get(rightBounds);
-        }
-
-        return null;
-    }
 }
 
diff --git a/src/org/broad/igv/sam/AlignmentInterval.java b/src/org/broad/igv/sam/AlignmentInterval.java
index 57dfcea..d4e9438 100644
--- a/src/org/broad/igv/sam/AlignmentInterval.java
+++ b/src/org/broad/igv/sam/AlignmentInterval.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,139 +23,133 @@
 package org.broad.igv.sam;
 
 import org.apache.log4j.Logger;
+import org.broad.igv.feature.Locus;
 import org.broad.igv.feature.SequenceManager;
+import org.broad.igv.sam.reader.CachingQueryReader;
+import org.broad.igv.session.ViewContext;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
 
 /**
  * @author jrobinso
  */
-public class AlignmentInterval {
+public class AlignmentInterval extends Locus {
 
     private static Logger log = Logger.getLogger(AlignmentInterval.class);
 
+    private List<AlignmentInterval.Row> alignmentRows;
+    List<CachingQueryReader.AlignmentCounts> counts;
     String genomeId;
-    String chr;
-    private int start;
-    private int end;
     byte[] reference;
-    // counts
-    int[] posA;
-    int[] posT;
-    int[] posC;
-    int[] posG;
-    int[] posN;
-    int[] negA;
-    int[] negT;
-    int[] negC;
-    int[] negG;
-    int[] negN;
-    int[] qA;
-    int[] qT;
-    int[] qC;
-    int[] qG;
-    int[] qN;
-    int[] posTotal;
-    int[] negTotal;
-    private int[] totalQ;
-    private int maxCount = 0;
-
-    public AlignmentInterval(String genomeId, String chr, int start, int end) {
-        this.genomeId = genomeId;
-        this.chr = chr;
-        this.start = start;
-        this.end = end;
+    int maxCount = 0;
 
+    public AlignmentInterval(String genomeId, String chr, int start, int end, List<Row> rows,
+                             List<CachingQueryReader.AlignmentCounts> counts) {
+        super(chr, start, end);
+        this.genomeId = genomeId;
+        this.alignmentRows = rows;
         reference = SequenceManager.readSequence(this.genomeId, chr, start, end);
-
-
-        int nPts = end - start;
-        posA = new int[nPts];
-        posT = new int[nPts];
-        posC = new int[nPts];
-        posG = new int[nPts];
-        posN = new int[nPts];
-        posTotal = new int[nPts];
-        negA = new int[nPts];
-        negT = new int[nPts];
-        negC = new int[nPts];
-        negG = new int[nPts];
-        negN = new int[nPts];
-        negTotal = new int[nPts];
-        qA = new int[nPts];
-        qT = new int[nPts];
-        qC = new int[nPts];
-        qG = new int[nPts];
-        qN = new int[nPts];
-        totalQ = new int[nPts];
+        this.counts = counts;
+        for(CachingQueryReader.AlignmentCounts c : counts) {
+            maxCount = Math.max(maxCount, c.getMaxCount());
+        }
     }
 
     boolean contains(String genomeId, String chr, int start, int end) {
-        return this.genomeId.equals(genomeId) && this.chr.equals(chr) && this.start <= start && this.end >= end;
+        return this.genomeId.equals(genomeId) && super.contains(chr, start, end);
     }
 
-    /**
-     * Weak version of contains, does not check genome
-     *
-     * @param chr
-     * @param start
-     * @param end
-     * @return
-     */
-    boolean contains(String chr, int start, int end) {
-        return this.chr.equals(chr) && this.start <= start && this.end >= end;
+
+    boolean overlaps(String genomeId, String chr, int start, int end) {
+        return this.genomeId.equals(genomeId) && overlaps(chr, start, end);
     }
 
-    public int getTotalCount(int pos) {
-        int offset = pos - start;
-        if (offset < 0 || offset >= posA.length) {
-            if (log.isDebugEnabled()) {
-                log.debug("Position out of range: " + pos + " (valid range - " + start + "-" + end);
+    public int getDepth() {
+        return alignmentRows.size();
+    }
+
+    static Alignment getFeatureContaining(List<Alignment> features, int right) {
+
+        int leftBounds = 0;
+        int rightBounds = features.size() - 1;
+        int idx = features.size() / 2;
+        int lastIdx = -1;
+
+        while (idx != lastIdx) {
+            lastIdx = idx;
+            Alignment f = features.get(idx);
+            if (f.contains(right)) {
+                return f;
             }
-            return 0;
-        } else {
-            return posTotal[offset] + negTotal[offset];
 
-        }
-    }
+            if (f.getStart() > right) {
+                rightBounds = idx;
+                idx = (leftBounds + idx) / 2;
+            } else {
+                leftBounds = idx;
+                idx = (rightBounds + idx) / 2;
 
-    public int getNegTotal(int pos) {
-        int offset = pos - start;
-        if (offset < 0 || offset >= posA.length) {
-            if (log.isDebugEnabled()) {
-                log.debug("Position out of range: " + pos + " (valid range - " + start + "-" + end);
             }
-            return 0;
-        } else {
-            return negTotal[offset];
 
         }
+        // Check the extremes
+        if (features.get(0).contains(right)) {
+            return features.get(0);
+        }
+
+        if (features.get(rightBounds).contains(right)) {
+            return features.get(rightBounds);
+        }
+
+        return null;
     }
 
-    public int getPosTotal(int pos) {
-        int offset = pos - start;
-        if (offset < 0 || offset >= posA.length) {
-            if (log.isDebugEnabled()) {
-                log.debug("Position out of range: " + pos + " (valid range - " + start + "-" + end);
-            }
-            return 0;
-        } else {
-            return posTotal[offset];
+    /**
+     * The "packed" alignments in this interval
+     */
+    public List<Row> getAlignmentRows() {
+        return alignmentRows;
+    }
 
-        }
+    public void setAlignmentRows(List<Row> alignmentRows) {
+        this.alignmentRows = alignmentRows;
     }
 
-    public int getTotalQuality(int pos) {
-        int offset = pos - start;
-        if (offset < 0 || offset >= posA.length) {
-            if (log.isDebugEnabled()) {
-                log.debug("Position out of range: " + pos + " (valid range - " + start + "-" + end);
-            }
-            return 0;
-        } else {
-            return totalQ[offset];
 
+    public void sortRows(AlignmentTrack.SortOption option) {
+        double center = ViewContext.getInstance().getCenter();
+        sortRows(option, center);
+    }
+
+
+    public void sortRows(AlignmentTrack.SortOption option, double location) {
+        if (alignmentRows == null) {
+            return;
+        }
+        for (AlignmentInterval.Row row : alignmentRows) {
+            if (option == AlignmentTrack.SortOption.NUCELOTIDE) {
+
+            }
+            row.updateScore(option, location, this);
         }
+
+        Collections.sort(alignmentRows, new Comparator<Row>() {
+
+            public int compare(AlignmentInterval.Row arg0, AlignmentInterval.Row arg1) {
+                if (arg0.getScore() > arg1.getScore()) {
+                    return 1;
+                } else if (arg0.getScore() > arg1.getScore()) {
+                    return -1;
+                }
+                return 0;
+            }
+        });
     }
 
+
     public byte getReference(int pos) {
         if (reference == null) {
             return 0;
@@ -171,244 +165,163 @@ public class AlignmentInterval {
         }
     }
 
+
     public int getCount(int pos, byte b) {
-        int offset = pos - start;
-        if (offset < 0 || offset >= posA.length) {
-            if (log.isDebugEnabled()) {
-                log.debug("Position out of range: " + pos + " (valid range - " + start + "-" + end);
-            }
-            return 0;
-        } else {
-            switch (b) {
-                case 'a':
-                case 'A':
-                    return posA[offset] + negA[offset];
-                case 't':
-                case 'T':
-                    return posT[offset] + negT[offset];
-                case 'c':
-                case 'C':
-                    return posC[offset] + negC[offset];
-                case 'g':
-                case 'G':
-                    return posG[offset] + negG[offset];
-                case 'n':
-                case 'N':
-                    return posN[offset] + negN[offset];
+        for (CachingQueryReader.AlignmentCounts c : counts) {
+            if (pos >= c.getStart() && pos < c.getEnd()) {
+                return c.getCount(pos, b);
             }
-            log.error("Unknown nucleotide: " + b);
-            return 0;
         }
+        return 0;
     }
 
-    public int getNegCount(int pos, byte b) {
-        int offset = pos - start;
-        if (offset < 0 || offset >= posA.length) {
-            if (log.isDebugEnabled()) {
-                log.debug("Position out of range: " + pos + " (valid range - " + start + "-" + end);
-            }
-            return 0;
-        } else {
-            switch (b) {
-                case 'a':
-                case 'A':
-                    return negA[offset];
-                case 't':
-                case 'T':
-                    return negT[offset];
-                case 'c':
-                case 'C':
-                    return negC[offset];
-                case 'g':
-                case 'G':
-                    return negG[offset];
-                case 'n':
-                case 'N':
-                    return negN[offset];
+    public int getMaxCount() {
+        return maxCount;
+    }
+
+    public int getTotalCount(int pos) {
+        for (CachingQueryReader.AlignmentCounts c : counts) {
+            if (pos >= c.getStart() && pos < c.getEnd()) {
+                return c.getTotalCount(pos);
             }
-            log.error("Unknown nucleotide: " + b);
-            return 0;
         }
+        return 0;
     }
 
-    public int getPosCount(int pos, byte b) {
-        int offset = pos - start;
-        if (offset < 0 || offset >= posA.length) {
-            if (log.isDebugEnabled()) {
-                log.debug("Position out of range: " + pos + " (valid range - " + start + "-" + end);
-            }
-            return 0;
-        } else {
-            switch (b) {
-                case 'a':
-                case 'A':
-                    return posA[offset];
-                case 't':
-                case 'T':
-                    return posT[offset];
-                case 'c':
-                case 'C':
-                    return posC[offset];
-                case 'g':
-                case 'G':
-                    return posG[offset];
-                case 'n':
-                case 'N':
-                    return posN[offset];
+    public int getNegCount(int pos, byte b) {
+        for (CachingQueryReader.AlignmentCounts c : counts) {
+            if (pos >= c.getStart() && pos < c.getEnd()) {
+                return c.getNegCount(pos, b);
             }
-            log.error("Unknown nucleotide: " + b);
-            return 0;
         }
-    }
+        return 0;
+     }
 
-    public int getQuality(int pos, byte b) {
-        int offset = pos - start;
-        if (offset < 0 || offset >= posA.length) {
-            log.error("Position out of range: " + pos + " (valid range - " + start + "-" + end);
-            return 0;
-        } else {
-            switch (b) {
-                case 'a':
-                case 'A':
-                    return qA[offset];
-                case 't':
-                case 'T':
-                    return qT[offset];
-                case 'c':
-                case 'C':
-                    return qC[offset];
-                case 'g':
-                case 'G':
-                    return qG[offset];
-                case 'n':
-                case 'N':
-                    return qN[offset];
+    public int getPosCount(int pos, byte b) {
+        for (CachingQueryReader.AlignmentCounts c : counts) {
+            if (pos >= c.getStart() && pos < c.getEnd()) {
+                return c.getPosCount(pos, b);
             }
-            log.error("Unknown nucleotide: " + posN);
-            return 0;
         }
-    }
+        return 0;
+     }
+
+    public static class Row {
+        int nextIdx;
+        private double score = 0;
+        List<Alignment> alignments;
+        private int start;
+        private int lastEnd;
+
+        public Row() {
+            nextIdx = 0;
+            this.alignments = new ArrayList(100);
+        }
 
-    private void incCounts(AlignmentBlock block, boolean isNegativeStrand) {
-        int start = block.getStart();
-        byte[] bases = block.getBases();
-        if (bases != null) {
-            for (int i = 0; i < bases.length; i++) {
-                int pos = start + i;
-                byte q = block.getQuality(i);
-                // TODO -- handle "="
-                byte n = bases[i];
-                incCount(pos, n, q, isNegativeStrand);
+        public void addAlignment(Alignment alignment) {
+            if (alignments.isEmpty()) {
+                this.start = alignment.getStart();
             }
+            alignments.add(alignment);
+            lastEnd = alignment.getEnd();
+
         }
-    }
 
-    // For alignments without blocks -- TODO refactor, this is ugly
-    void incCounts(Alignment alignment) {
-        int start = alignment.getAlignmentStart();
-        int end = alignment.getAlignmentEnd();
+        public void updateScore(AlignmentTrack.SortOption option, double center, AlignmentInterval interval) {
 
-        AlignmentBlock[] blocks = alignment.getAlignmentBlocks();
-        if (blocks != null) {
-            for (AlignmentBlock b : blocks) {
-                incCounts(b, alignment.isNegativeStrand());
-            }
-        } else {
-            for (int pos = start; pos < end; pos++) {
-                byte q = 0;
-                incCount(pos, (byte) 'n', q, alignment.isNegativeStrand());
+            int adjustedCenter = (int) center;
+            Alignment centerAlignment = getFeatureContaining(alignments, adjustedCenter);
+            if (centerAlignment == null) {
+                setScore(Double.MAX_VALUE);
+            } else {
+                switch (option) {
+                    case START:
+                        setScore(centerAlignment.getStart());
+                        break;
+                    case STRAND:
+                        setScore(centerAlignment.isNegativeStrand() ? -1 : 1);
+                        break;
+                    case NUCELOTIDE:
+                        byte base = centerAlignment.getBase(adjustedCenter);
+                        byte ref = interval.getReference(adjustedCenter);
+                        if (base == 'N' || base == 'n') {
+                            setScore(Integer.MAX_VALUE - 1);
+                        } else if (base == ref) {
+                            setScore(Integer.MAX_VALUE);
+                        } else {
+                            int count = interval.getCount(adjustedCenter, base);
+                            byte phred = centerAlignment.getPhred(adjustedCenter);
+                            float score = -(count + (phred / 100.0f));
+                            setScore(score);
+                        }
+                        break;
+                    case QUALITY:
+                        setScore(-centerAlignment.getMappingQuality());
+                        break;
+                    case SAMPLE:
+                        String sample = centerAlignment.getSample();
+                        score = sample == null ? 0 : sample.hashCode();
+                        setScore(score);
+                        break;
+                    case READ_GROUP:
+                        String readGroup = centerAlignment.getReadGroup();
+                        score = readGroup == null ? 0 : readGroup.hashCode();
+                        setScore(score);
+                        break;
+                }
             }
         }
-    }
 
-    void incCount(int pos, byte b, byte q, boolean isNegativeStrand) {
 
-        int offset = pos - start;
-        if (offset > 0 && offset < posA.length) {
-            switch (b) {
-                case 'a':
-                case 'A':
-                    if (isNegativeStrand) {
-                        negA[offset] = negA[offset] + 1;
-                    } else {
-                        posA[offset] = posA[offset] + 1;
-                    }
-                    qA[offset] = qA[offset] + q;
-                    break;
-                case 't':
-                case 'T':
-                    if (isNegativeStrand) {
-                        negT[offset] = negT[offset] + 1;
-                    } else {
-                        posT[offset] = posT[offset] + 1;
-                    }
-                    qT[offset] = qT[offset] + q;
-                    break;
-                case 'c':
-                case 'C':
-                    if (isNegativeStrand) {
-                        negC[offset] = negC[offset] + 1;
-                    } else {
-                        posC[offset] = posC[offset] + 1;
-                    }
-                    qC[offset] = qC[offset] + q;
-                    break;
-                case 'g':
-                case 'G':
-                    if (isNegativeStrand) {
-                        negG[offset] = negG[offset] + 1;
-                    } else {
-                        posG[offset] = posG[offset] + 1;
-                    }
-                    qG[offset] = qG[offset] + q;
-                    break;
-                case 'n':
-                case 'N':
-                    if (isNegativeStrand) {
-                        negN[offset] = negN[offset] + 1;
-                    } else {
-                        posN[offset] = posN[offset] + 1;
-                    }
-                    qN[offset] = qN[offset] + q;
+        // Used for iterating over all alignments, e.g. for packing
 
+        public Alignment nextAlignment() {
+            if (nextIdx < alignments.size()) {
+                Alignment tmp = alignments.get(nextIdx);
+                nextIdx++;
+                return tmp;
+            } else {
+                return null;
             }
-            if (isNegativeStrand) {
-                negTotal[offset] = negTotal[offset] + 1;
+        }
+
+        public int getNextStartPos() {
+            if (nextIdx < alignments.size()) {
+                return alignments.get(nextIdx).getStart();
             } else {
-                posTotal[offset] = posTotal[offset] + 1;
+                return Integer.MAX_VALUE;
             }
-            totalQ[offset] = totalQ[offset] + q;
+        }
 
-            maxCount = Math.max(posTotal[offset] + negTotal[offset], maxCount);
+        public boolean hasNext() {
+            return nextIdx < alignments.size();
         }
-    }
 
-    /**
-     * @return the start
-     */
-    public int getStart() {
-        return start;
-    }
+        public void resetIdx() {
+            nextIdx = 0;
+        }
 
-    /**
-     * @return the end
-     */
-    public int getEnd() {
-        return end;
-    }
+        /**
+         * @return the score
+         */
+        public double getScore() {
+            return score;
+        }
 
-    /**
-     * @return the totalQ
-     */
-    public int[] getTotalQ() {
-        return totalQ;
-    }
+        /**
+         * @param score the score to set
+         */
+        public void setScore(double score) {
+            this.score = score;
+        }
 
-    /**
-     * @return the maxCount
-     */
-    public int getMaxCount() {
-        return maxCount;
+        public int getStart() {
+            return start;
+        }
+
+        public int getLastEnd() {
+            return lastEnd;
+        }
     }
 }
 
diff --git a/src/org/broad/igv/sam/AlignmentLoader.java b/src/org/broad/igv/sam/AlignmentLoader.java
new file mode 100644
index 0000000..2006b89
--- /dev/null
+++ b/src/org/broad/igv/sam/AlignmentLoader.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.sam;
+
+import net.sf.samtools.util.CloseableIterator;
+import org.apache.log4j.Logger;
+import org.broad.igv.PreferenceManager;
+import org.broad.igv.sam.AlignmentInterval.Row;
+import org.broad.igv.sam.reader.ReadGroupFilter;
+
+import java.util.*;
+
+/**
+ * A utility class to experiment with alignment packing methods.
+ * <p/>
+ * Numbers:
+ * <p/>
+ * packAlignments1  (original)
+ * Packed  19075 out of 19075 in 59 rows in:     0.046 seconds
+ * Packed  111748 out of 431053 in 1000 rows in: 14.313 seconds
+ * <p/>
+ * packAlignments2  (row by row)
+ * Packed 17027 out of 17027 in 58 rows:        0.035 seconds
+ * Packed 113061 out of 431053 in 1000 rows in  8.276 seconds
+ * :
+ * packAlignments2b  (row by row with hash)
+ * Packed 15274 out of 15274 in 58 rows in:     0.011 seconds
+ * Packed 101595 out of 423736 in 1000 rows in: 0.177 seconds
+ * <p/>
+ * packAlignments3  (priority queue)
+ * Packed 19075 out of 19075 in 63 rows in:      0.044 seconds
+ * Packed 104251 out of 430716 in 1000 rows in:  0.108 seconds
+ *
+ * @author jrobinso
+ */
+public class AlignmentLoader {
+
+    private static Logger log = Logger.getLogger(AlignmentLoader.class);
+
+    /**
+     * Minimum gap between the end of one alignment and start of another.
+     */
+    public static final int MIN_ALIGNMENT_SPACING = 5;
+
+    /**
+     * A temporary working buffer
+     */
+    static Alignment[] buffer = new Alignment[1000000];
+
+
+    /**
+     * Allocates each alignment to the rows such that there is no overlap.
+     *
+     * @param iter             Iterator wrapping the collection of alignments
+
+     * @param maxLevels        the maximum number of levels (rows) to create
+     */
+    public static List<AlignmentInterval.Row> packAlignments(
+            CloseableIterator<Alignment> iter,
+            int maxLevels,
+            int end) {
+
+ 
+        List<Row> alignmentRows = new ArrayList(maxLevels);
+        if (iter == null || !iter.hasNext()) {
+            return alignmentRows;
+        }
+
+
+        // Compares 2 alignments by length.
+        Comparator lengthComparator = new Comparator<Alignment>() {
+
+            public int compare(Alignment row1, Alignment row2) {
+                return (row2.getEnd() - row2.getStart()) -
+                        (row1.getEnd() - row2.getStart());
+
+            }
+        };
+
+
+        // Strictly speaking we should loop discarding dupes, etc.
+        Alignment firstAlignment = iter.next();
+        int start = firstAlignment.getStart();
+        int bucketCount = end - start + 1;
+
+        // Create buckets.  We use priority queues to keep the buckets sorted by alignment length.  However this
+        // is probably a neeedless complication,  any collection type would do. 
+        PriorityQueue[] bucketArray = new PriorityQueue[bucketCount];
+        PriorityQueue firstBucket = new PriorityQueue(5, lengthComparator);
+        bucketArray[0] = firstBucket;
+        firstBucket.add(firstAlignment);
+        int totalCount = 1;
+
+        //  Allocate alignments to buckets based on position
+        List<Alignment> matesUnmapped = new ArrayList(1000);
+        Map<String, Alignment> unmappedReads = new HashMap(1000);
+
+        while (iter.hasNext()) {
+            Alignment alignment = iter.next();
+
+            // Store unmapped reads in a hache, to be used later to fetch sequence
+            if (!alignment.isMapped()) {
+                unmappedReads.put(alignment.getReadName(), alignment);
+
+            } else  {
+
+
+                // We can get negative buckets if softclipping is on as the alignments are only approximately
+                // sorted.  Throw all alignments < start in the first bucket.
+                int bucketNumber = Math.max(0, alignment.getStart() - start);
+                if (bucketNumber < bucketCount) {
+                    PriorityQueue bucket = bucketArray[bucketNumber];
+                    if (bucket == null) {
+                        bucket = new PriorityQueue<Alignment>(5, lengthComparator);
+                        bucketArray[bucketNumber] = bucket;
+                    }
+                    bucket.add(alignment);
+                    totalCount++;
+                } else {
+                    log.debug("Alignment out of bounds: " + alignment.getStart() + " (> " + end);
+                }
+
+                if (alignment.getMate() != null && !alignment.getMate().isMapped()) {
+                    matesUnmapped.add(alignment);
+                }
+            }
+        }
+
+        // Get unmapped mate sequences
+        for (Alignment a : matesUnmapped) {
+            Alignment mate = unmappedReads.get(a.getReadName());
+            if (mate != null) {
+                a.setMateSequence(mate.getReadSequence());
+            }
+        }
+
+
+        // Allocate alignments to rows
+        long t0 = System.currentTimeMillis();
+        int allocatedCount = 0;
+        int nextStart = start;
+        AlignmentInterval.Row currentRow = new Row();
+        while (allocatedCount < totalCount) { // && alignmentRows.size() < maxLevels) {
+
+            // Loop through alignments until we reach the end of the interval
+            while (nextStart <= end) {
+                PriorityQueue<Alignment> bucket = null;
+
+                // Advance to next occupied bucket
+                while (bucket == null && nextStart <= end) {
+                    int bucketNumber = nextStart - start;
+                    bucket = bucketArray[bucketNumber];
+                    if (bucket == null) {
+                        nextStart++;
+                    }
+                }
+
+                // Pull the next alignment out of the bucket and add to the current row
+                if (bucket != null) {
+                    Alignment alignment = bucket.remove();
+                    if (bucket.isEmpty()) {
+                        bucketArray[nextStart - start] = null;
+                    }
+                    currentRow.addAlignment(alignment);
+                    nextStart = currentRow.getLastEnd() + MIN_ALIGNMENT_SPACING;
+                    allocatedCount++;
+                }
+            }
+
+            // We've reached the end of the interval,  start a new row
+            if (currentRow.alignments.size() > 0) {
+                alignmentRows.add(currentRow);
+            }
+
+            if(alignmentRows.size() >= maxLevels) {
+                break;
+            }
+            currentRow = new Row();
+            nextStart = start;
+        }
+        if (log.isDebugEnabled()) {
+            long dt = System.currentTimeMillis() - t0;
+            log.debug("Packed alignments in " + dt);
+        }
+
+        // Add the last row
+        if (currentRow.alignments.size() > 0 && alignmentRows.size() < maxLevels) {
+            alignmentRows.add(currentRow);
+        }
+
+
+        return alignmentRows;
+
+    }
+
+
+}
diff --git a/src/org/broad/igv/sam/AlignmentPacker.java b/src/org/broad/igv/sam/AlignmentPacker.java
deleted file mode 100644
index 88cb935..0000000
--- a/src/org/broad/igv/sam/AlignmentPacker.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.sam;
-
-import net.sf.samtools.util.CloseableIterator;
-import org.apache.log4j.Logger;
-import org.broad.igv.sam.AlignmentDataManager.Row;
-import org.broad.igv.sam.reader.ReadGroupFilter;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.PriorityQueue;
-
-/**
- * A utility class to experiment with alignment packing methods.
- * <p/>
- * Numbers:
- * <p/>
- * packAlignments1  (original)
- * Packed  19075 out of 19075 in 59 rows in:     0.046 seconds
- * Packed  111748 out of 431053 in 1000 rows in: 14.313 seconds
- * <p/>
- * packAlignments2  (row by row)
- * Packed 17027 out of 17027 in 58 rows:        0.035 seconds
- * Packed 113061 out of 431053 in 1000 rows in  8.276 seconds
- * :
- * packAlignments2b  (row by row with hash)
- * Packed 15274 out of 15274 in 58 rows in:     0.011 seconds
- * Packed 101595 out of 423736 in 1000 rows in: 0.177 seconds
- * <p/>
- * packAlignments3  (priority queue)
- * Packed 19075 out of 19075 in 63 rows in:      0.044 seconds
- * Packed 104251 out of 430716 in 1000 rows in:  0.108 seconds
- *
- * @author jrobinso
- */
-public class AlignmentPacker {
-
-    private static Logger log = Logger.getLogger(AlignmentPacker.class);
-
-    /**
-     * Minimum gap between the end of one alignment and start of another.
-     */
-    public static final int MIN_ALIGNMENT_SPACING = 5;
-
-    /**
-     * A temporary working buffer
-     */
-    static Alignment[] buffer = new Alignment[1000000];
-
-
-    /**
-     * Allocates each alignment to the rows such that there is no overlap.
-     *
-     * @param iter             Iterator wrapping the collection of alignments
-     * @param showDuplicates   indicates alignments marked "duplicate" are shown
-     * @param qualityThreshold alignment mapQ threshold.  Alignments with lower values are not shown
-     * @param maxLevels        the maximum number of levels (rows) to create
-     */
-    public static List<AlignmentDataManager.Row> packAlignments(
-            CloseableIterator<Alignment> iter,
-            boolean showDuplicates,
-            int qualityThreshold,
-            int maxLevels,
-            int end) {
-
-
-        // TODO -- the filter should be generalized, and passed in as a parameter
-        ReadGroupFilter filter = ReadGroupFilter.getFilter();
-
-        List<AlignmentDataManager.Row> alignmentRows = new ArrayList(maxLevels);
-        if (iter == null || !iter.hasNext()) {
-            return alignmentRows;
-        }
-
-
-        // Compares 2 alignments by length.
-        Comparator lengthComparator = new Comparator<Alignment>() {
-
-            public int compare(Alignment row1, Alignment row2) {
-                return (row2.getEnd() - row2.getAlignmentStart()) -
-                        (row1.getEnd() - row2.getAlignmentStart());
-
-            }
-        };
-
-
-        // Strictly speaking we should loop discarding dupes, etc.
-        Alignment firstAlignment = iter.next();
-        int start = firstAlignment.getAlignmentStart();
-        int bucketCount = end - start + 1;
-
-        // Create buckets.  We use priority queues to keep the buckets sorted by alignment length.  However this
-        // is probably a neeedless complication,  any collection type would do. 
-        PriorityQueue[] bucketArray = new PriorityQueue[bucketCount];
-        PriorityQueue firstBucket = new PriorityQueue(5, lengthComparator);
-        bucketArray[0] = firstBucket;
-        firstBucket.add(firstAlignment);
-        int totalCount = 1;
-
-        //  Allocate alignments to buckets based on position
-        while (iter.hasNext()) {
-            Alignment alignment = iter.next();
-            if ((showDuplicates || !alignment.isDuplicate()) &&
-                    alignment.getMappingQuality() >= qualityThreshold &&
-                    alignment.isMapped() &&
-                    (filter == null || filter.filterAlignment(alignment) == false)) {
-
-
-                int bucketNumber = alignment.getAlignmentStart() - start;
-
-                if (bucketNumber < bucketCount) {
-                    PriorityQueue bucket = bucketArray[bucketNumber];
-                    if (bucket == null) {
-                        bucket = new PriorityQueue<Alignment>(5, lengthComparator);
-                        bucketArray[bucketNumber] = bucket;
-                    }
-                    bucket.add(alignment);
-                    totalCount++;
-                } else {
-                    log.debug("Alignment out of bounds: " + alignment.getAlignmentStart() + " (> " + end);
-                }
-            }
-        }
-
-
-        // Allocate alignments to rows
-
-        int allocatedCount = 0;
-        int nextStart = start;
-        Row currentRow = new Row();
-        while (allocatedCount < totalCount && alignmentRows.size() < maxLevels) {
-
-            // Loop through alignments until we reach the end of the interval
-            while (nextStart <= end) {
-                PriorityQueue<Alignment> bucket = null;
-
-                // Advance to next occupied bucket
-                while (bucket == null && nextStart <= end) {
-                    int bucketNumber = nextStart - start;
-                    bucket = bucketArray[bucketNumber];
-                    if (bucket == null) {
-                        nextStart++;
-                    }
-                }
-
-                // Pull the next alignment out of the bucket and add to the current row
-                if (bucket != null) {
-                    Alignment alignment = bucket.remove();
-                    if (bucket.isEmpty()) {
-                        bucketArray[nextStart - start] = null;
-                    }
-                    currentRow.addAlignment(alignment);
-                    nextStart = currentRow.getLastEnd() + MIN_ALIGNMENT_SPACING;
-                    allocatedCount++;
-                }
-            }
-
-            // We've reached the end of the interval,  start a new row
-            if (currentRow.alignments.size() > 0) {
-                alignmentRows.add(currentRow);
-            }
-            currentRow = new Row();
-            nextStart = start;
-        }
-        return alignmentRows;
-
-    }
-
-
-}
diff --git a/src/org/broad/igv/sam/AlignmentRenderer.java b/src/org/broad/igv/sam/AlignmentRenderer.java
index da3d3d3..8f32048 100644
--- a/src/org/broad/igv/sam/AlignmentRenderer.java
+++ b/src/org/broad/igv/sam/AlignmentRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -17,16 +17,15 @@
  */
 package org.broad.igv.sam;
 
-import cern.colt.map.OpenIntObjectHashMap;
 import org.broad.igv.PreferenceManager;
 import org.broad.igv.feature.SequenceManager;
+import org.broad.igv.feature.Strand;
 import org.broad.igv.renderer.GraphicUtils;
-import org.broad.igv.renderer.SequenceRenderer;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.track.RenderContext;
 import org.broad.igv.ui.FontManager;
-import org.broad.igv.ui.IGVModel;
-import org.broad.igv.ui.ViewContext;
 import org.broad.igv.util.ChromosomeColors;
+import org.broad.igv.util.ColorUtilities;
 
 import java.awt.*;
 import java.util.HashMap;
@@ -38,7 +37,11 @@ import java.util.Map;
  */
 public class AlignmentRenderer implements FeatureRenderer {
 
-    public static Map<Character, Color> nucleotideColors = new HashMap();
+    private static Map<Character, Color> nucleotideColors;
+
+    // Static because all alignment tracks are in color space, or none are
+    public static boolean colorSpace;
+
     private static Color purple = new Color(118, 24, 220);
     private static Color deletionColor = Color.black;
     private static Color skippedColor = new Color(150, 184, 200);
@@ -49,7 +52,21 @@ public class AlignmentRenderer implements FeatureRenderer {
     private static Color grey2 = new Color(165, 165, 165);
     public static Color grey1 = new Color(200, 200, 200);
 
-    static {
+    private static HashMap<String, Color> readGroupColors = new HashMap();
+
+    static Font font = FontManager.getScalableFont(10);
+    private ViewContext viewContext;
+    private static Stroke thickStroke = new BasicStroke(2.0f);
+    private static float dash[] = {4.0f, 1.0f};
+    private static Stroke dashedStroke = new BasicStroke(2.0f, BasicStroke.CAP_BUTT,
+            BasicStroke.JOIN_MITER, 1.0f, dash, 0.0f);
+
+    private static final Color negStrandColor = new Color(110, 145, 225);
+    private static final Color posStrandColor = new Color(165, 35, 39);
+    PreferenceManager.SAMPreferences prefs;
+
+    synchronized static void initColorMap() {
+        nucleotideColors = new HashMap();
         nucleotideColors.put('A', Color.GREEN);
         nucleotideColors.put('a', Color.GREEN);
         nucleotideColors.put('C', Color.BLUE);
@@ -60,36 +77,40 @@ public class AlignmentRenderer implements FeatureRenderer {
         nucleotideColors.put('g', new Color(209, 113, 5));
         nucleotideColors.put('N', Color.gray.brighter());
         nucleotideColors.put('n', Color.gray.brighter());
+
     }
 
-    static Font font = FontManager.getScalableFont(10);
-    private ViewContext viewContext;
-    private SequenceRenderer seqRenderer;
-    private static Stroke thickStroke = new BasicStroke(2.0f);
-    private static float dash[] = {4.0f, 1.0f};
-    private static Stroke dashedStroke = new BasicStroke(2.0f, BasicStroke.CAP_BUTT,
-            BasicStroke.JOIN_MITER, 1.0f, dash, 0.0f);
-    private static final Color negStrandColor = new Color(110, 145, 225);
-    private static final Color posStrandColor = new Color(165, 35, 39);
 
-    /**
-     * Constructs ...
-     */
     public AlignmentRenderer() {
-        this.viewContext = IGVModel.getInstance().getViewContext();
-        this.seqRenderer = new SequenceRenderer();
+        this.viewContext = ViewContext.getInstance();
+        this.prefs = PreferenceManager.getInstance().getSAMPreferences();
+        if (nucleotideColors == null) {
+            initColorMap();
+        }
 
     }
 
+
+    public static Map<Character, Color> getNucleotideColors() {
+        if (nucleotideColors == null) {
+            initColorMap();
+        }
+        return nucleotideColors;
+    }
+
     /**
      * Render a list of alignments in the given rectangle.
      */
-    public void renderAlignments(List<Alignment> alignments, RenderContext context, Rectangle rect,
+    public void renderAlignments(List<Alignment> alignments,
+                                 RenderContext context,
+                                 Rectangle rect,
                                  AlignmentTrack.RenderOptions renderOptions,
-                                 boolean leaveMargin, Map<String, Color> selectedReadNames) {
+                                 boolean leaveMargin,
+                                 Map<String, Color> selectedReadNames) {
 
         double origin = context.getOrigin();
         double locScale = context.getScale();
+        Rectangle screenRect = context.getVisibleRect();
 
         if ((alignments != null) && (alignments.size() > 0)) {
 
@@ -102,7 +123,15 @@ public class AlignmentRenderer implements FeatureRenderer {
                 double pixelStart = ((alignment.getStart() - origin) / locScale);
                 double pixelEnd = ((alignment.getEnd() - origin) / locScale);
 
-                // If the any part of the feature fits in the track rectangle draw  it
+                // If the any part of the feature fits in the track rectangle draw it.  If we've passed the end of
+                // the rect break
+                if (pixelEnd < rect.getX()) {
+                    continue;
+                }
+                else if(pixelStart > (rect.getMaxX() + 100)) {
+                    break;
+                }
+
                 if ((pixelEnd >= rect.getX()) && (pixelStart <= rect.getMaxX())) {
                     Color alignmentColor = getAlignmentColor(alignment, locScale, viewContext.getCenter(), renderOptions);
 
@@ -117,16 +146,14 @@ public class AlignmentRenderer implements FeatureRenderer {
                         int y = (int) (rect.getY() + (rect.getHeight() - h) / 2);
                         g.fillRect((int) pixelStart, y, w, h);
                     } else {
-                        drawAlignment(alignment,
-                                rect, g, context, alignmentColor,
-                                renderOptions, leaveMargin, selectedReadNames);
+                        drawAlignment(alignment, rect, g, context, alignmentColor, renderOptions, leaveMargin, selectedReadNames);
                     }
 
                 }
             }
 
             // Draw posA border around the center base
-            if (locScale < 5 && renderOptions.shadeCenters) {
+            if (locScale < 5 && renderOptions.isShadeCenters()) {
                 // Calculate center lines
                 double center = (int) (viewContext.getCenter() - origin);
                 int centerLeftP = (int) (center / locScale);
@@ -146,8 +173,11 @@ public class AlignmentRenderer implements FeatureRenderer {
     /**
      * Method for drawing alignments without "blocks" (e.g. DotAlignedAlignment)
      */
-    private void drawSimpleAlignment(Alignment alignment, Rectangle rect,
-                                     Graphics2D g, RenderContext context, boolean flagUnmappedPair) {
+    private void drawSimpleAlignment(Alignment alignment,
+                                     Rectangle rect,
+                                     Graphics2D g,
+                                     RenderContext context,
+                                     boolean flagUnmappedPair) {
         double origin = context.getOrigin();
         double locScale = context.getScale();
         int x = (int) ((alignment.getStart() - origin) / locScale);
@@ -158,9 +188,36 @@ public class AlignmentRenderer implements FeatureRenderer {
         int arrowLength = Math.min(5, w / 6);
         int[] xPoly = null;
         int[] yPoly = {y, y, y + h / 2, y + h, y + h};
+
+        // Don't draw off edge of clipping rect
+        if (x < rect.x && (x + w) > (rect.x + rect.width)) {
+            x = rect.x;
+            w = rect.width;
+            arrowLength = 0;
+        } else if (x < rect.x) {
+            int delta = rect.x - x;
+            x = rect.x;
+            w -= delta;
+            if (alignment.isNegativeStrand()) {
+                arrowLength = 0;
+            }
+        } else if ((x + w) > (rect.x + rect.width)) {
+            w -= ((x + w) - (rect.x + rect.width));
+            if (!alignment.isNegativeStrand()) {
+                arrowLength = 0;
+            }
+        }
+
+
         if (alignment.isNegativeStrand()) {
+            //     2     1
+            //   3
+            //     5     5
             xPoly = new int[]{x + w, x, x - arrowLength, x, x + w};
         } else {
+            //     1     2
+            //             3
+            //     5     4
             xPoly = new int[]{x, x + w, x + w + arrowLength, x + w, x};
         }
         g.fillPolygon(xPoly, yPoly, xPoly.length);
@@ -172,18 +229,22 @@ public class AlignmentRenderer implements FeatureRenderer {
     }
 
     /**
-     * Draw a single alignment
-     *
      * @param alignment
-     * @param rect           -- the bounding rectangle.  Used to bound the height of the alignment
-     * @param g              -- the GraphicContext on which to draw
-     * @param context        -- rendering context, contains misc parameters
-     * @param alignmentColor -- color of the alignment,  used for computing base color with alpha
+     * @param rect
+     * @param g
+     * @param context
+     * @param alignmentColor
+     * @param renderOptions
+     * @param leaveMargin
+     * @param selectedReadNames
      */
     private void drawAlignment(
             Alignment alignment,
-            Rectangle rect, Graphics2D g, RenderContext context,
-            Color alignmentColor, AlignmentTrack.RenderOptions renderOptions,
+            Rectangle rect,
+            Graphics2D g,
+            RenderContext context,
+            Color alignmentColor,
+            AlignmentTrack.RenderOptions renderOptions,
             boolean leaveMargin,
             Map<String, Color> selectedReadNames) {
 
@@ -192,7 +253,7 @@ public class AlignmentRenderer implements FeatureRenderer {
         AlignmentBlock[] blocks = alignment.getAlignmentBlocks();
 
         if (blocks == null) {
-            drawSimpleAlignment(alignment, rect, g, context, renderOptions.flagUnmappedPairs);
+            drawSimpleAlignment(alignment, rect, g, context, renderOptions.isFlagUnmappedPairs());
             return;
         }
 
@@ -213,7 +274,7 @@ public class AlignmentRenderer implements FeatureRenderer {
             int h = (int) Math.max(1, rect.getHeight() - (leaveMargin ? 2 : 0));
             int y = (int) (rect.getY()); // + (rect.getHeight() - h) / 2);
 
-            // Create polygon to represent the alignment. 
+            // Create polygon to represent the alignment.
             boolean isZeroQuality = alignment.getMappingQuality() == 0;
 
             if (highZoom && w > 10) {
@@ -221,82 +282,120 @@ public class AlignmentRenderer implements FeatureRenderer {
                 w -= 2;
             }
 
-            if (w <= 10 || h <= 10 || aBlock != terminalBlock) {
-                g.fillRect(x, y, w, h);
-                if (isZeroQuality) {
-                    greyGraphics.drawRect(x, y, w - 1, h);
-                }
+            // If block is out of view skip
+            if (x + w >= rect.x && x <= rect.getMaxX()) {
+                if (w <= 10 || h <= 10 || aBlock != terminalBlock) {
+                    g.fillRect(x, y, w, h);
+                    if (isZeroQuality) {
+                        greyGraphics.drawRect(x, y, w - 1, h);
+                    }
 
-                if (renderOptions.flagUnmappedPairs && alignment.isPaired() && !alignment.getMate().isMapped()) {
-                    Graphics2D cRed = context.getGraphic2DForColor(Color.red);
-                    cRed.drawRect(x, y, w, h);
-                }
+                    if (renderOptions.isFlagUnmappedPairs() && alignment.isPaired() && !alignment.getMate().isMapped()) {
+                        Graphics2D cRed = context.getGraphic2DForColor(Color.red);
+                        cRed.drawRect(x, y, w, h);
+                    }
 
-                if (selectedReadNames.containsKey(alignment.getReadName())) {
-                    Color c = selectedReadNames.get(alignment.getReadName());
-                    if (c == null) {
-                        c = Color.blue;
+                    if (selectedReadNames.containsKey(alignment.getReadName())) {
+                        Color c = selectedReadNames.get(alignment.getReadName());
+                        if (c == null) {
+                            c = Color.blue;
+                        }
+                        Graphics2D cBlue = context.getGraphic2DForColor(c);
+                        Stroke s = cBlue.getStroke();
+                        cBlue.setStroke(thickStroke);
+                        cBlue.drawRect(x, y, w, h);
+                        cBlue.setStroke(s);
                     }
-                    Graphics2D cBlue = context.getGraphic2DForColor(c);
-                    Stroke s = cBlue.getStroke();
-                    cBlue.setStroke(thickStroke);
-                    cBlue.drawRect(x, y, w, h);
-                    cBlue.setStroke(s);
-                }
 
-            } else {
-                int arrowLength = Math.min(5, w / 6);
-                int[] xPoly = null;
-                int[] yPoly = {y, y, y + h / 2, y + h, y + h};
-                if (alignment.isNegativeStrand()) {
-                    xPoly = new int[]{x + w, x, x - arrowLength, x, x + w};
                 } else {
-                    xPoly = new int[]{x, x + w, x + w + arrowLength, x + w, x};
-                }
-                g.fillPolygon(xPoly, yPoly, xPoly.length);
-                if (isZeroQuality) {
-                    greyGraphics.drawPolygon(xPoly, yPoly, xPoly.length);
-                }
+                    int arrowLength = Math.min(5, w / 6);
+
+                    // Don't draw off edge of clipping rect
+                    if (x < rect.x && (x + w) > (rect.x + rect.width)) {
+                        x = rect.x;
+                        w = rect.width;
+                        arrowLength = 0;
+                    } else if (x < rect.x) {
+                        int delta = rect.x - x;
+                        x = rect.x;
+                        w -= delta;
+                        if (alignment.isNegativeStrand()) {
+                            arrowLength = 0;
+                        }
+                    } else if ((x + w) > (rect.x + rect.width)) {
+                        w -= ((x + w) - (rect.x + rect.width));
+                        if (!alignment.isNegativeStrand()) {
+                            arrowLength = 0;
+                        }
+                    }
 
-                if (renderOptions.flagUnmappedPairs && alignment.isPaired() && !alignment.getMate().isMapped()) {
-                    Graphics2D cRed = context.getGraphic2DForColor(Color.red);
-                    cRed.drawPolygon(xPoly, yPoly, xPoly.length);
-                }
+                    int[] xPoly = null;
+                    int[] yPoly = {y, y, y + h / 2, y + h, y + h};
 
-                if (selectedReadNames.containsKey(alignment.getReadName())) {
-                    Color c = selectedReadNames.get(alignment.getReadName());
-                    if (c == null) {
-                        c = Color.blue;
+                    if (alignment.isNegativeStrand()) {
+                        xPoly = new int[]{x + w, x, x - arrowLength, x, x + w};
+                    } else {
+                        xPoly = new int[]{x, x + w, x + w + arrowLength, x + w, x};
+                    }
+                    g.fillPolygon(xPoly, yPoly, xPoly.length);
+
+
+                    if (isZeroQuality) {
+                        greyGraphics.drawPolygon(xPoly, yPoly, xPoly.length);
+                    }
+
+                if (renderOptions.isFlagUnmappedPairs() && alignment.isPaired() && !alignment.getMate().isMapped()) {
+                        Graphics2D cRed = context.getGraphic2DForColor(Color.red);
+                        cRed.drawPolygon(xPoly, yPoly, xPoly.length);
+                    }
+
+                    if (selectedReadNames.containsKey(alignment.getReadName())) {
+                        Color c = selectedReadNames.get(alignment.getReadName());
+                        if (c == null) {
+                            c = Color.blue;
+                        }
+                        Graphics2D cBlue = context.getGraphic2DForColor(c);
+                        Stroke s = cBlue.getStroke();
+                        cBlue.setStroke(thickStroke);
+                        cBlue.drawPolygon(xPoly, yPoly, xPoly.length);
+                        cBlue.setStroke(s);
                     }
-                    Graphics2D cBlue = context.getGraphic2DForColor(c);
-                    Stroke s = cBlue.getStroke();
-                    cBlue.setStroke(thickStroke);
-                    cBlue.drawPolygon(xPoly, yPoly, xPoly.length);
-                    cBlue.setStroke(s);
                 }
             }
 
-            if (lastBlockEnd > Integer.MIN_VALUE) {
+
+            if (locScale < 5) {
+                drawBases(context, rect, aBlock, alignmentColor, renderOptions.isShadeBases(), renderOptions.isShowAllBases());
+            }
+
+            // Draw connecting lines between blocks, if in view
+            if (lastBlockEnd > Integer.MIN_VALUE && x > rect.x) {
                 Graphics2D gLine;
                 Stroke stroke;
                 int gapIdx = blockNumber - 1;
-                Color gapLineColor = deletionColor;
                 if (gapTypes != null && gapIdx < gapTypes.length && gapTypes[gapIdx] == SamAlignment.SKIPPED_REGION) {
                     gLine = context.getGraphic2DForColor(skippedColor);
                     stroke = gLine.getStroke();
                 } else {
-                    gLine = context.getGraphic2DForColor(gapLineColor);
+                    gLine = context.getGraphic2DForColor(deletionColor);
                     stroke = gLine.getStroke();
-                    gLine.setStroke(dashedStroke);
+                    //gLine.setStroke(dashedStroke);
+                    gLine.setStroke(thickStroke);
                 }
-                gLine.drawLine(lastBlockEnd, y + h / 2, x, y + h / 2);
+
+                int startX = Math.max(rect.x, lastBlockEnd);
+                int endX = Math.min(rect.x + rect.width, x);
+
+                gLine.drawLine(startX, y + h / 2, endX, y + h / 2);
                 gLine.setStroke(stroke);
             }
             lastBlockEnd = x + w;
 
-            if (locScale < 5) {
-                drawBases(context, rect, aBlock, alignmentColor, renderOptions.shadeBases, renderOptions.showAllBases);
+            // Next block cannot start before lastBlockEnd.  If its out of view we are done.
+            if(lastBlockEnd > rect.getMaxX()) {
+                break;
             }
+
         }
 
         // Render insertions if locScale ~ 0.25 (base level)
@@ -311,17 +410,23 @@ public class AlignmentRenderer implements FeatureRenderer {
      * @param context
      * @param rect
      */
-    private void drawBases(RenderContext context, Rectangle rect, AlignmentBlock block, Color alignmentColor,
-                           boolean shadeBases, boolean showAllBases) {
+    private void drawBases(RenderContext context,
+                           Rectangle rect,
+                           AlignmentBlock block,
+                           Color alignmentColor,
+                           boolean shadeBases,
+                           boolean showAllBases) {
 
         alignmentColor.getRGBColorComponents(alignComps);
 
         double locScale = context.getScale();
         double origin = context.getOrigin();
         String chr = context.getChr();
-        String genome = IGVModel.getInstance().getViewContext().getGenomeId();
+        String genome = context.getGenomeId();
 
         byte[] read = block.getBases();
+        boolean isSoftClipped = block.isSoftClipped();
+
         if ((read != null) && (read.length > 0)) {
 
             // Compute bounds, get posA graphics to use,  and compute posA font
@@ -335,10 +440,9 @@ public class AlignmentRenderer implements FeatureRenderer {
             }
 
             // Get the base qualities, start/end,  and reference sequence
-            
             int start = block.getStart();
             int end = start + read.length;
-            byte[] reference = SequenceManager.readSequence(genome, chr, start, end);
+            byte[] reference = isSoftClipped ? null : SequenceManager.readSequence(genome, chr, start, end);
 
 
             // Loop through base pair coordinates
@@ -348,47 +452,43 @@ public class AlignmentRenderer implements FeatureRenderer {
                 // the start of this block
                 int idx = loc - start;
 
-                if(read[idx] == 'N') {
-                    System.out.println();
-                }
-
                 // Is this base posA mismatch?  Note '=' means indicates posA match by definition
-                // If we do not have posA valid reference we assume posA match
+                // If we do not have posA valid reference we assume posA match.  Soft clipped
+                // bases are considered mismatched by definition
                 boolean misMatch =
-                        read[idx] != '=' &&
-                                reference != null &&
-                                idx < reference.length &&
-                                reference[idx] != 0 &&
-                                reference[idx] != read[idx];
+                        isSoftClipped ||
+                                (read[idx] != '=' &&
+                                        reference != null &&
+                                        idx < reference.length &&
+                                        reference[idx] != 0 &&
+                                        reference[idx] != read[idx]);
 
                 if (misMatch || showAllBases) {
                     char c = (char) read[loc - start];
+
+
                     Color color = nucleotideColors.get(c);
                     if (color == null) {
                         color = Color.black;
                     }
-                    PreferenceManager.SAMPreferences prefs = PreferenceManager.getInstance().getSAMPreferences();
+
                     if (shadeBases) {
-                        float alpha = 0;
                         byte qual = block.getQuality(loc - start);
-                        int minQ = prefs.getBaseQualityMin();
-                        color.getRGBColorComponents(colorComps);
-                        if (qual < minQ) {
-                            alpha = 0.1f;
-                        } else {
-                            int maxQ = prefs.getBaseQualityMax();
-                            alpha = Math.max(0.1f, Math.min(1.0f, 0.1f + 0.9f * (qual - minQ) / (maxQ - minQ)));
-                        }
-
-                        // Round alpha to nearest 0.1, for effeciency;
-                        alpha = ((int) (alpha * 10 + 0.5f)) / 10.0f;
-                        color = getCompositeColor(alignComps, colorComps, alpha);
+                        color = getShadedColor(qual, color, prefs);
                     }
 
 
                     // If there is room for text draw the character, otherwise
                     // just draw posA rectangle to represent the
                     int pX0 = (int) ((loc - origin) / locScale);
+
+                    // Don't draw out of clipping rect
+                    if (pX0 > rect.getMaxX()) {
+                        break;
+                    } else if (pX0 + dX < rect.getX()) {
+                        continue;
+                    }
+
                     if ((dX >= 8) && (dY >= 12)) {
                         g.setColor(color);
                         drawCenteredText(g, new char[]{c}, pX0, pY + 1, dX, dY - 2);
@@ -411,6 +511,23 @@ public class AlignmentRenderer implements FeatureRenderer {
         }
     }
 
+    private Color getShadedColor(byte qual, Color color, PreferenceManager.SAMPreferences prefs) {
+        float alpha = 0;
+        int minQ = prefs.getBaseQualityMin();
+        color.getRGBColorComponents(colorComps);
+        if (qual < minQ) {
+            alpha = 0.1f;
+        } else {
+            int maxQ = prefs.getBaseQualityMax();
+            alpha = Math.max(0.1f, Math.min(1.0f, 0.1f + 0.9f * (qual - minQ) / (maxQ - minQ)));
+        }
+
+        // Round alpha to nearest 0.1, for effeciency;
+        alpha = ((int) (alpha * 10 + 0.5f)) / 10.0f;
+        color = ColorUtilities.getCompositeColor(alignComps, colorComps, alpha);
+        return color;
+    }
+
     private void drawCenteredText(Graphics2D g, char[] chars, int x, int y, int w, int h) {
 
         // Get measures needed to center the message
@@ -446,6 +563,14 @@ public class AlignmentRenderer implements FeatureRenderer {
                 int h = (int) Math.max(1, rect.getHeight() - 2);
                 int y = (int) (rect.getY() + (rect.getHeight() - h) / 2);
 
+                // Don't draw out of clipping rect
+                if (x > rect.getMaxX()) {
+                    break;
+                } else if (x < rect.getX()) {
+                    continue;
+                }
+
+
                 gInsertion.fillRect(x - 2, y, 4, 2);
                 gInsertion.fillRect(x - 1, y, 2, h);
                 gInsertion.fillRect(x - 2, y + h - 2, 4, 2);
@@ -457,70 +582,160 @@ public class AlignmentRenderer implements FeatureRenderer {
                                     double center, AlignmentTrack.RenderOptions renderOptions) {
 
         // Set color used to draw the feature.  Highlight features that intersect the
-        // center line.  Also update row "score" if alignment intersects center line
+        // center line.  Also restorePersistentState row "score" if alignment intersects center line
 
-        Color c = grey1;
-        if (renderOptions.colorByStrand) {
-            if (alignment.isNegativeStrand()) {
-                c = negStrandColor;
-            } else {
-                c = posStrandColor;
-            }
+        Color c = alignment.getDefaultColor();
 
-        } else if (renderOptions.shadeCenters && center >= alignment.getStart() && center <= alignment.getEnd()) {
-            if (locScale < 1) {
-                c = grey2;
-            }
-        }
 
-        if (alignment.getMappingQuality() > 0) {
-            if (alignment.isPaired() && alignment.getMate().isMapped()) {
-                boolean sameChr = alignment.getMate().mateChr.equals(alignment.getChromosome());
-                if (sameChr) {
-                    int readDistance = Math.abs(alignment.getInferredInsertSize());
-                    if (readDistance > renderOptions.insertSizeThreshold) {
+        switch (renderOptions.getColorOption()) {
+            case INSERT_SIZE:
+                if (alignment.isPaired() && alignment.getMate().isMapped()) {
+                    boolean sameChr = alignment.getMate().mateChr.equals(alignment.getChr());
+                    if (sameChr) {
+                        int readDistance = Math.abs(alignment.getInferredInsertSize());
+                        if (readDistance > renderOptions.getInsertSizeThreshold()) {
+                            c = ChromosomeColors.getColor(alignment.getMate().mateChr);
+                            //c = getDistanceColor(readDistance);
+                        }
+                    } else {
                         c = ChromosomeColors.getColor(alignment.getMate().mateChr);
-                        //c = getDistanceColor(readDistance);
+                        if (c == null) {
+                            c = Color.black;
+                        }
                     }
+                }
+                break;
+            case PAIR_ORIENTATION:
+                c = getOrientationColor(alignment);
+                break;
+            case READ_STRAND:
+                if (alignment.isNegativeStrand()) {
+                    c = negStrandColor;
                 } else {
-                    c = ChromosomeColors.getColor(alignment.getMate().mateChr);
+                    c = posStrandColor;
+                }
+                break;
+            case FRAGMENT_STRAND:
+                if (alignment.getFragmentStrand(1) == Strand.NEGATIVE) {
+                    c = negStrandColor;
+                } else if (alignment.getFragmentStrand(1) == Strand.POSITIVE) {
+                    c = posStrandColor;
+                }
+                break;
+            case READ_GROUP:
+                String rg = alignment.getReadGroup();
+                if (rg != null) {
+                    c = readGroupColors.get(rg);
                     if (c == null) {
-                        c = Color.black;
+                        c = ColorUtilities.randomColor(readGroupColors.size() + 1);
+                        readGroupColors.put(rg, c);
                     }
                 }
-            }
-        } else {
+                break;
+            case SAMPLE:
+                String sample = alignment.getSample();
+                if (sample != null) {
+                    c = readGroupColors.get(sample);
+                    if (c == null) {
+                        c = ColorUtilities.randomColor(readGroupColors.size() + 1);
+                        readGroupColors.put(sample, c);
+                    }
+                }
+                break;
+            default:
+                if (renderOptions.isShadeCenters() && center >= alignment.getStart() && center <= alignment.getEnd()) {
+                    if (locScale < 1) {
+                        c = grey2;
+                    }
+                }
+        }
+
+        if (alignment.getMappingQuality() == 0) {
             // Maping Q = 0
             float alpha = 0.15f;
             c.getColorComponents(rbgBuffer);
             // Assuming white background TODO -- this should probably be passed in
-            return getCompositeColor(whiteComponents, rbgBuffer, alpha);
+            return ColorUtilities.getCompositeColor(whiteComponents, rbgBuffer, alpha);
         }
 
         return c;
     }
 
+
     /**
-     * Compute a composite color using alpha transparency rules
+     * Illumin scheme -- todo, something for Solid
+     *
+     * @return
      */
-    // TODO -- move this method and cache to posA utility class
-    private static OpenIntObjectHashMap colorMap = new OpenIntObjectHashMap(1000);
-
-    private static Color getCompositeColor(float[] dest, float[] source, float alpha) {
-        int r = (int) ((alpha * source[0] + (1 - alpha) * dest[0]) * 255 + 0.5);
-        int g = (int) ((alpha * source[1] + (1 - alpha) * dest[1]) * 255 + 0.5);
-        int b = (int) ((alpha * source[2] + (1 - alpha) * dest[2]) * 255 + 0.5);
-        int a = 255;
-        int value = ((a & 0xFF) << 24) |
-                ((r & 0xFF) << 16) |
-                ((g & 0xFF) << 8) |
-                ((b & 0xFF) << 0);
-
-        Color c = (Color) colorMap.get(value);
-        if (c == null) {
-            c = new Color(value);
-            colorMap.put(value, c);
+
+    private Color getOrientationColor(Alignment alignment) {
+
+        Color c = null;
+        if (!alignment.isProperPair() && !alignment.isSmallInsert()) {
+            if (alignment.getAttribute("CS") != null) {
+                c = getSolidScheme().get(alignment.getPairOrientation());
+            } else {
+                c = getIlluminaScheme().get(alignment.getPairOrientation());
+            }
         }
-        return c;
+
+        return c == null ? grey1 : c;
+
     }
+
+    private static final Color LR_COLOR = grey1; // "Normal" alignment color
+    private static final Color RL_COLOR = new Color(0, 150, 0);
+    private static final Color RR_COLOR = new Color(0, 0, 150);
+    private static final Color LL_COLOR = new Color(0, 150, 150);
+
+    Map<String, Color> illuminaScheme;
+    Map<String, Color> solidScheme;
+
+    private Map<String, Color> getIlluminaScheme() {
+        if (illuminaScheme == null) {
+            illuminaScheme = new HashMap();
+            //LR
+            illuminaScheme.put("F1R2", LR_COLOR);
+            illuminaScheme.put("F2R1", LR_COLOR);
+            illuminaScheme.put("F R ", LR_COLOR);
+            illuminaScheme.put("FR", LR_COLOR);
+            //LL
+            illuminaScheme.put("F1F2", LL_COLOR);
+            illuminaScheme.put("F2F1", LL_COLOR);
+            illuminaScheme.put("F F ", LL_COLOR);
+            illuminaScheme.put("FF", LL_COLOR);
+            //RR
+            illuminaScheme.put("R1R2", RR_COLOR);
+            illuminaScheme.put("R2R1", RR_COLOR);
+            illuminaScheme.put("R R ", RR_COLOR);
+            illuminaScheme.put("RR", RR_COLOR);
+            //RL
+            illuminaScheme.put("R1F2", RL_COLOR);
+            illuminaScheme.put("R2F1", RL_COLOR);
+            illuminaScheme.put("R F ", RL_COLOR);
+            illuminaScheme.put("RF", RR_COLOR);
+        }
+        return illuminaScheme;
+    }
+
+
+    private Map<String, Color> getSolidScheme() {
+        if (solidScheme == null) {
+            solidScheme = new HashMap();
+            //LR
+            solidScheme.put("F1F2", LR_COLOR);
+            solidScheme.put("R2R1", LR_COLOR);
+            //LL -- switched with RR color per Bob's instructions
+            solidScheme.put("F1R2", RR_COLOR);
+            solidScheme.put("R2F1", RR_COLOR);
+            //RR
+            solidScheme.put("R1F2", LL_COLOR);
+            solidScheme.put("F2R1", LL_COLOR);
+            //RL
+            solidScheme.put("R1R2", RL_COLOR);
+            solidScheme.put("F2F1", RL_COLOR);
+        }
+        return solidScheme;
+    }
+
 }
diff --git a/src/org/broad/igv/sam/AlignmentTrack.java b/src/org/broad/igv/sam/AlignmentTrack.java
index 171f409..a5fe7fb 100644
--- a/src/org/broad/igv/sam/AlignmentTrack.java
+++ b/src/org/broad/igv/sam/AlignmentTrack.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -20,26 +20,22 @@ package org.broad.igv.sam;
 //~--- non-JDK imports --------------------------------------------------------
 
 import com.jidesoft.swing.JidePopupMenu;
-import net.sf.samtools.SAMFileHeader;
-import net.sf.samtools.SAMSequenceRecord;
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
 import org.broad.igv.PreferenceManager;
 import org.broad.igv.feature.FeatureUtils;
 import org.broad.igv.renderer.GraphicUtils;
 import org.broad.igv.renderer.Renderer;
-import org.broad.igv.sam.reader.AlignmentQueryReader;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.tdf.TDFDataSource;
 import org.broad.igv.tdf.TDFReader;
 import org.broad.igv.track.*;
-import org.broad.igv.ui.GuiUtilities;
 import org.broad.igv.ui.IGVMainFrame;
-import org.broad.igv.ui.IGVModel;
-import org.broad.igv.ui.ViewContext;
+import org.broad.igv.ui.UIConstants;
 import org.broad.igv.ui.panel.DragEvent;
 import org.broad.igv.ui.panel.DragListener;
 import org.broad.igv.ui.util.FileChooserDialog;
 import org.broad.igv.ui.util.MessageUtils;
+import org.broad.igv.ui.util.UIUtilities;
 import org.broad.igv.util.ColorUtilities;
 import org.broad.igv.util.ResourceLocator;
 
@@ -51,50 +47,59 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.MouseEvent;
 import java.io.File;
-import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.*;
 import java.util.List;
-import java.util.Map;
 
 /**
  * @author jrobinso
  */
 public class AlignmentTrack extends AbstractTrack implements DragListener {
 
+    public enum SortOption {
+        START, STRAND, NUCELOTIDE, QUALITY, SAMPLE, READ_GROUP
+    }
+
+    public enum ColorOption {
+        INSERT_SIZE, READ_STRAND, FRAGMENT_STRAND, PAIR_ORIENTATION, SAMPLE, READ_GROUP;
+    }
+
     public static final int MIN_ALIGNMENT_SPACING = 10;
-    SequenceTrack sequenceTrack;
+    static final ColorOption DEFAULT_COLOR_OPTION = ColorOption.INSERT_SIZE;
+    static final boolean DEFAULT_SHOWALLBASES = false;
+
+    private static ColorOption colorByOption = null;
 
+    private SequenceTrack sequenceTrack;
 
-    CoverageTrack coverageTrack;
+    private CoverageTrack coverageTrack;
 
-    RenderOptions renderOptions;
+    private RenderOptions renderOptions;
 
     private static Logger log = Logger.getLogger(AlignmentTrack.class);
     private int expandedHeight = 14;
     private int collapsedHeight = 2;
-    int height;
-    FeatureRenderer renderer;
-    String lastChromosomeName = null;
-    int bamIntervalWidth = 16000;
-    double minVisibleScale = 25;
-    Rectangle renderedRect;
-    HashMap<String, Color> selectedReadNames = new HashMap();
+    private FeatureRenderer renderer;
+    private double minVisibleScale = 25;
+    private Rectangle renderedRect;
     private int minHeight = 100;
-    AlignmentDataManager dataManager;
+    private AlignmentDataManager dataManager;
 
+    private HashMap<String, Color> selectedReadNames = new HashMap();
+    int selectionColorIndex = 0;
 
-    public enum SortOption {
-
-        START, STRAND, NUCELOTIDE, QUALITY, SAMPLE, READ_GROUP
+    public CoverageTrack getCoverageTrack() {
+        return coverageTrack;
     }
 
+
     public static class RenderOptions {
-        boolean shadeBases;
-        boolean shadeCenters;
-        boolean flagUnmappedPairs;
-        boolean showAllBases;
-        int insertSizeThreshold;
-        boolean colorByStrand;
+        private boolean changedByUser = false;
+        private boolean shadeBases;
+        private boolean shadeCenters;
+        private boolean flagUnmappedPairs;
+        private boolean showAllBases;
+        private int insertSizeThreshold;
+        private ColorOption colorOption;
 
         RenderOptions() {
             PreferenceManager.SAMPreferences prefs = PreferenceManager.getInstance().getSAMPreferences();
@@ -102,19 +107,138 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
             shadeCenters = prefs.isShadeCenter();
             flagUnmappedPairs = prefs.isFlagUnmappedPair();
             insertSizeThreshold = prefs.getInsertSizeThreshold();
-            showAllBases = false;
-            colorByStrand = false;
+            showAllBases = DEFAULT_SHOWALLBASES;
+            colorOption = colorByOption;
+        }
+
+        /**
+         * Called by session writer.  Return instance variable values as a map of strings.  Used to record current state
+         * of object.   Variables with default values are not stored, as it is presumed the user has not changed them.
+         *
+         * @return
+         */
+        public Map<String, String> getPersistentState() {
+            Map<String, String> attributes = new HashMap();
+            PreferenceManager.SAMPreferences prefs = PreferenceManager.getInstance().getSAMPreferences();
+            if (isShadeBases() != prefs.isShadeBaseQuality()) {
+                attributes.put("shadeBases", String.valueOf(isShadeBases()));
+            }
+            if (isShadeCenters() != prefs.isShadeCenter()) {
+                attributes.put("shadeCenters", String.valueOf(isShadeBases()));
+            }
+            if (isFlagUnmappedPairs() != prefs.isFlagUnmappedPair()) {
+                attributes.put("flagUnmappedPairs", String.valueOf(isFlagUnmappedPairs()));
+            }
+            if (getInsertSizeThreshold() != prefs.getInsertSizeThreshold()) {
+                attributes.put("insertSizeThreshold", String.valueOf(getInsertSizeThreshold()));
+            }
+            if (isShowAllBases() != DEFAULT_SHOWALLBASES) {
+                attributes.put("showAllBases", String.valueOf(isShowAllBases()));
+            }
+            if (getColorOption() != DEFAULT_COLOR_OPTION) {
+                attributes.put("colorOption", colorByOption.toString());
+            }
+
+            return attributes;
+        }
+
+        /**
+         * Called by session reader.  Restores state of object.
+         *
+         * @param attributes
+         */
+        public void restorePersistentState(Map<String, String> attributes) {
+
+            String value;
+            value = attributes.get("insertSizeThreshold");
+            if (value != null) {
+                setInsertSizeThreshold(Integer.parseInt(value));
+            }
+            value = attributes.get("shadeBases");
+            if (value != null) {
+                setShadeBases(Boolean.parseBoolean(value));
+            }
+            value = attributes.get("shadeCenters");
+            if (value != null) {
+                setShadeCenters(Boolean.parseBoolean(value));
+            }
+            value = attributes.get("flagUnmappedPairs");
+            if (value != null) {
+                setFlagUnmappedPairs(Boolean.parseBoolean(value));
+            }
+            value = attributes.get("showAllBases");
+            if (value != null) {
+                setShowAllBases(Boolean.parseBoolean(value));
+            }
+            value = attributes.get("colorOption");
+            if (value != null) {
+                setColorOption(ColorOption.valueOf(value));
+                colorByOption = getColorOption();
+            }
+        }
+
+        public boolean isShadeBases() {
+            return shadeBases;
+        }
+
+        public void setShadeBases(boolean shadeBases) {
+            changedByUser = true;
+            this.shadeBases = shadeBases;
+        }
+
+        public boolean isShadeCenters() {
+            return shadeCenters;
+        }
+
+        public void setShadeCenters(boolean shadeCenters) {
+            changedByUser = true;
+            this.shadeCenters = shadeCenters;
+        }
+
+        public boolean isFlagUnmappedPairs() {
+            return flagUnmappedPairs;
+        }
+
+        public void setFlagUnmappedPairs(boolean flagUnmappedPairs) {
+            changedByUser = true;
+            this.flagUnmappedPairs = flagUnmappedPairs;
+        }
+
+        public boolean isShowAllBases() {
+            return showAllBases;
+        }
+
+        public void setShowAllBases(boolean showAllBases) {
+            changedByUser = true;
+            this.showAllBases = showAllBases;
+        }
+
+        public int getInsertSizeThreshold() {
+            return insertSizeThreshold;
+        }
+
+        public void setInsertSizeThreshold(int insertSizeThreshold) {
+            changedByUser = true;
+            this.insertSizeThreshold = insertSizeThreshold;
+        }
+
+        public ColorOption getColorOption() {
+            return colorOption;
         }
 
+        public void setColorOption(ColorOption colorOption) {
+            changedByUser = true;
+            this.colorOption = colorOption;
+        }
     }
 
 
-    public AlignmentTrack(ResourceLocator locator, String name, AlignmentDataManager dataManager) {
-        super(locator, name);
+    public AlignmentTrack(ResourceLocator locator, AlignmentDataManager dataManager) {
+        super(locator);
 
-        //AlignmentQueryReader reader = SamQueryReaderFactory.getReader(locator);
         this.dataManager = dataManager;
 
+
         float maxRange = PreferenceManager.getInstance().getSAMPreferences().getMaxVisibleRange();
         minVisibleScale = (maxRange * 1000) / 700;
 
@@ -130,27 +254,23 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
         renderOptions = new RenderOptions();
 
 
-        AlignmentQueryReader reader = dataManager.getReader();
-        SAMFileHeader header = reader.getHeader();
-        if (header != null) {
-            List<SAMSequenceRecord> records = header.getSequenceDictionary().getSequences();
-            if (records.size() > 0) {
-                boolean ensembleChrConventions = true;
-                for (SAMSequenceRecord rec : header.getSequenceDictionary().getSequences()) {
-                    if (rec.getSequenceName().contains("chr")) {
-                        ensembleChrConventions = false;
-                    }
+        if (colorByOption == null) {
+            String colorByString = PreferenceManager.getInstance().getSAMPreferences().getColorBy();
+            if (colorByString == null) {
+                colorByOption = DEFAULT_COLOR_OPTION;
+            } else {
+                try {
+                    colorByOption = ColorOption.valueOf(colorByString);
+                }
+                catch (Exception e) {
+                    log.error("Error setting color option", e);
+                    colorByOption = DEFAULT_COLOR_OPTION;
+
                 }
-                dataManager.setEnsembleChrConventions(ensembleChrConventions);
             }
         }
     }
 
-
-    public CoverageTrack getCoverageTrack() {
-        return coverageTrack;
-    }
-
     public void setCoverageTrack(CoverageTrack coverageTrack) {
         this.coverageTrack = coverageTrack;
     }
@@ -181,8 +301,6 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
     }
 
     private int getNLevels() {
-
-
         return (dataManager.getAlignmentRows() == null ? 1 : dataManager.getAlignmentRows().size());
     }
 
@@ -211,8 +329,8 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
 
         if (context.getScale() > minVisibleScale) {
 
-            Graphics2D g = context.getGraphic2DForColor(Color.black);
-            GraphicUtils.drawCenteredText("Zoom in to see alignments.", rect, g);
+            Graphics2D g = context.getGraphic2DForColor(Color.gray);
+            GraphicUtils.drawCenteredText("Zoom in to see alignments.", context.getVisibleRect(), g);
             return;
         }
 
@@ -221,14 +339,18 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
 
     private void renderFeatures(RenderContext context, Rectangle inputRect) {
         try {
+            log.debug("Render features");
+            final String genomeId = context.getGenomeId();
+            final String chr = context.getChr();
+            final int start = (int) context.getOrigin();
+            final int end = (int) context.getEndLocation() + 1;
+
+            List<AlignmentInterval.Row> tmp = dataManager.getAlignmentRows(genomeId, chr, start, end);
 
-            if (dataManager.getAlignmentRows() == null) {
+            if (tmp == null) {
                 return;
             }
 
-            PreferenceManager.SAMPreferences prefs = PreferenceManager.getInstance().getSAMPreferences();
-            int maxLevels = prefs.getMaxLevels();
-
             Rectangle visibleRect = context.getVisibleRect();
 
             // Divide rectangle into equal height levels
@@ -243,16 +365,17 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
 
             int levelNumber = 0;
             // levelList is copied to prevent concurrent modification exception
-            List<AlignmentDataManager.Row> tmp = new ArrayList(dataManager.getAlignmentRows());
-            for (AlignmentDataManager.Row row : tmp) {
+            //List<AlignmentDataManager.Row> tmp = new ArrayList(dataManager.getAlignmentRows());
 
-                if ((visibleRect != null && y > visibleRect.getMaxY()) ||
-                        levelNumber > maxLevels) {
+            for (AlignmentInterval.Row row : tmp) {
+
+                if ((visibleRect != null && y > visibleRect.getMaxY()) || levelNumber > dataManager.getMaxLevels()) {
                     return;
                 }
 
                 if (y + h > visibleRect.getY()) {
                     Rectangle rect = new Rectangle(inputRect.x, (int) y, inputRect.width, (int) h);
+                    renderOptions.setColorOption(colorByOption);
                     renderer.renderAlignments(row.alignments,
                             context,
                             rect,
@@ -273,24 +396,12 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
     }
 
     public void reloadData() {
-        dataManager.setLoadedInterval(null);
-        preloadData();
-
-    }
-
-    public void preloadData() {
-        ViewContext vc = IGVModel.getInstance().getViewContext();
-        dataManager.preloadData(vc.getChrName(), (int) vc.getOrigin(), (int) vc.getEnd(), vc.getZoom());
+        float maxRange = PreferenceManager.getInstance().getSAMPreferences().getMaxVisibleRange();
+        minVisibleScale = (maxRange * 1000) / 700;
+        renderOptions = new RenderOptions();
+        dataManager.reload();
     }
 
-    public boolean isLoading = false;
-
-    @Override
-    public synchronized void preloadData(final String chr,
-                                         final int start, final int end, int zoom) {
-
-        dataManager.preloadData(chr, start, end, zoom);
-    }
 
     /**
      * Sort alignment rows such that alignments that intersect from the
@@ -300,6 +411,10 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
         dataManager.sortRows(option);
     }
 
+    public void sortRows(SortOption option, double location) {
+        dataManager.sortRows(option, location);
+    }
+
     public void packAlignments() {
         dataManager.packAlignments();
     }
@@ -316,7 +431,7 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
             StringBuffer buf = new StringBuffer();
             buf.append(alignment.getValueString(location, null).replace("<br>", "\n"));
             buf.append("\n");
-            buf.append("Alignment start position = " + alignment.getChromosome() + ":" + (alignment.getAlignmentStart() + 1));
+            buf.append("Alignment start position = " + alignment.getChr() + ":" + (alignment.getAlignmentStart() + 1));
             buf.append("\n");
             buf.append(alignment.getReadSequence());
             StringSelection stringSelection = new StringSelection(buf.toString());
@@ -339,7 +454,8 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
             if (mate != null && mate.isMapped()) {
                 String chr = mate.mateChr;
                 int start = mate.mateStart - 1;
-                IGVModel.getInstance().getViewContext().centerOnLocation(chr, start);
+                ViewContext.getInstance().centerOnLocation(chr, start);
+                ViewContext.getInstance().recordHistory();
             }
         }
 
@@ -358,6 +474,7 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
     }
 
     // SamTracks use posA custom renderer, not derived from Renderer
+
     public Renderer getRenderer() {
         return null;
     }
@@ -393,11 +510,11 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
             return null;
         }
 
-        AlignmentDataManager.Row row = dataManager.getAlignmentRows().get(levelNumber);
+        AlignmentInterval.Row row = dataManager.getAlignmentRows().get(levelNumber);
         List<Alignment> features = row.alignments;
 
         // give posA 2 pixel window, otherwise very narrow features will be missed.
-        double bpPerPixel = IGVModel.getInstance().getViewContext().getScale();
+        double bpPerPixel = ViewContext.getInstance().getScale();
         double minWidth = 2 * bpPerPixel;    /* * */
         return (Alignment) FeatureUtils.getFeatureAt(position, minWidth, features);
 
@@ -406,7 +523,7 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
     public void dragStopped(DragEvent evt) {
         // Disabled.  Not sure why we ever thought this was posA good idea
         //if (PreferenceManager.getInstance().getSAMPreferences().isAutosort() &&
-        //        IGVModel.getInstance().getViewContext().getScale() < 1) {
+        //        ViewContext.getInstance().getScale() < 1) {
         //    sortRows(SortOption.START);
         //    IGVMainFrame.getInstance().repaintDataPanels();
         //}
@@ -429,12 +546,10 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
 
             if (f.getStart() > right) {
                 rightBounds = idx;
-                idx =
-                        (leftBounds + idx) / 2;
+                idx = (leftBounds + idx) / 2;
             } else {
                 leftBounds = idx;
-                idx =
-                        (rightBounds + idx) / 2;
+                idx = (rightBounds + idx) / 2;
 
             }
 
@@ -460,7 +575,7 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
             if (e.isShiftDown() || e.isAltDown() || (e.getClickCount() > 1)) {
                 return super.handleClick(e);
             } else if (e.getButton() == MouseEvent.BUTTON1 &&
-                    (IGVConstants.IS_MAC && e.isMetaDown() || (!IGVConstants.IS_MAC && e.isControlDown()))) {
+                    (UIConstants.IS_MAC && e.isMetaDown() || (!UIConstants.IS_MAC && e.isControlDown()))) {
                 double location = getViewContext().getChromosomePosition(e.getX());
                 double displayLocation = location + 1;
                 Alignment alignment = this.getAlignmentAt(displayLocation, e.getY());
@@ -468,9 +583,14 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
                     if (selectedReadNames.containsKey(alignment.getReadName())) {
                         selectedReadNames.remove(alignment.getReadName());
                     } else {
-                        Color c = alignment.isPaired() && alignment.getMate() != null && alignment.getMate().isMapped() ? ColorUtilities.randomColor(selectedReadNames.size() + 1) : Color.black;
+                        Color c = alignment.isPaired() && alignment.getMate() != null && alignment.getMate().isMapped() ?
+                                ColorUtilities.randomColor(selectionColorIndex++) : Color.black;
                         selectedReadNames.put(alignment.getReadName(), c);
                     }
+                    Object source = e.getSource();
+                    if (source instanceof JComponent) {
+                        ((JComponent) source).repaint();
+                    }
                 }
                 return true;
             }
@@ -478,12 +598,11 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
         return false;
     }
 
-    public JPopupMenu getPopupMenu(
-            final MouseEvent evt) {
+    public JPopupMenu getPopupMenu(final MouseEvent evt) {
 
         JPopupMenu popupMenu = new JidePopupMenu();
 
-        JLabel popupTitle = new JLabel("  " + getDisplayName(), JLabel.CENTER);
+        JLabel popupTitle = new JLabel("  " + getName(), JLabel.CENTER);
 
         Font newFont = popupMenu.getFont().deriveFont(Font.BOLD, 12);
         popupTitle.setFont(newFont);
@@ -493,12 +612,13 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
 
         addSortMenuItem(popupMenu);
         addPackMenuItem(popupMenu);
+        addCoverageDepthMenuItem(popupMenu);
         popupMenu.addSeparator();
+        addColorByMenuItem(popupMenu);
 
         addShadeBaseMenuItem(popupMenu);
         addShadeCentersMenuItem(popupMenu);
-        addColorByStrandMenuItem(popupMenu);
-        addCopyToClipboardItem(popupMenu, evt);
+
         addGoToMate(popupMenu, evt);
         addShowAllBasesMenuItem(popupMenu);
         addInsertSizeMenuItem(popupMenu);
@@ -507,19 +627,25 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
         addShowCoverageItem(popupMenu);
         addLoadCoverageDataItem(popupMenu);
 
+        addCopyToClipboardItem(popupMenu, evt);
         popupMenu.addSeparator();
 
-        JLabel trackSettingsHeading = new JLabel("  Track Settings",
-                JLabel.LEFT);
+        addSelecteByNameItem(popupMenu);
+        popupMenu.addSeparator();
+
+        JLabel trackSettingsHeading = new JLabel("  Track Settings", JLabel.LEFT);
         trackSettingsHeading.setFont(newFont);
 
         popupMenu.add(trackSettingsHeading);
 
-        TrackMenuUtils.addTrackRenameItem(popupMenu);
 
-        TrackMenuUtils.addExpandCollapseItem(popupMenu);
+        Collection<Track> tmp = new ArrayList();
+        tmp.add(this);
+        popupMenu.add(TrackMenuUtils.getTrackRenameItem(tmp));
+
+        popupMenu.add(TrackMenuUtils.getExpandCollapseItem(tmp));
 
-        TrackMenuUtils.addRemoveMenuItem(popupMenu);
+        popupMenu.add(TrackMenuUtils.getRemoveMenuItem(tmp));
 
         popupMenu.addSeparator();
         addClearSelectionsMenuItem(popupMenu);
@@ -527,6 +653,23 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
         return popupMenu;
     }
 
+    public void addSelecteByNameItem(JPopupMenu menu) {
+        // Change track height by attribute
+        JMenuItem item = new JMenuItem("Select by name...");
+        item.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                String val = MessageUtils.showInputDialog("Enter read name: ");
+                if (val != null && val.trim().length() > 0) {
+                    selectedReadNames.put(val, ColorUtilities.randomColor(selectedReadNames.size() + 1));
+                    refresh();
+                }
+            }
+        });
+
+        menu.add(item);
+    }
+
     public void addSortMenuItem(JPopupMenu menu) {
         // Change track height by attribute
         JMenu item = new JMenu("Sort alignments");
@@ -535,13 +678,9 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
         m1.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
+                IGVMainFrame.getInstance().getTrackManager().sortAlignmentTracks(SortOption.START);
+                refresh();
 
-                    public void run() {
-                        TrackManager.getInstance().sortAlignmentTracks(SortOption.START);
-                        IGVMainFrame.getInstance().repaintDataPanels();
-                    }
-                });
             }
         });
 
@@ -549,13 +688,9 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
         m2.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
+                IGVMainFrame.getInstance().getTrackManager().sortAlignmentTracks(SortOption.STRAND);
+                refresh();
 
-                    public void run() {
-                        TrackManager.getInstance().sortAlignmentTracks(SortOption.STRAND);
-                        IGVMainFrame.getInstance().repaintDataPanels();
-                    }
-                });
             }
         });
 
@@ -563,13 +698,10 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
         m3.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
 
-                    public void run() {
-                        TrackManager.getInstance().sortAlignmentTracks(SortOption.NUCELOTIDE);
-                        IGVMainFrame.getInstance().repaintDataPanels();
-                    }
-                });
+                IGVMainFrame.getInstance().getTrackManager().sortAlignmentTracks(SortOption.NUCELOTIDE);
+                refresh();
+
             }
         });
 
@@ -577,13 +709,10 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
         m4.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
 
-                    public void run() {
-                        TrackManager.getInstance().sortAlignmentTracks(SortOption.QUALITY);
-                        IGVMainFrame.getInstance().repaintDataPanels();
-                    }
-                });
+                IGVMainFrame.getInstance().getTrackManager().sortAlignmentTracks(SortOption.QUALITY);
+                refresh();
+
             }
         });
 
@@ -591,13 +720,10 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
         m5.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
 
-                    public void run() {
-                        TrackManager.getInstance().sortAlignmentTracks(SortOption.SAMPLE);
-                        IGVMainFrame.getInstance().repaintDataPanels();
-                    }
-                });
+                IGVMainFrame.getInstance().getTrackManager().sortAlignmentTracks(SortOption.SAMPLE);
+                refresh();
+
             }
         });
 
@@ -605,17 +731,15 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
         m6.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
 
-                    public void run() {
-                        TrackManager.getInstance().sortAlignmentTracks(SortOption.READ_GROUP);
-                        IGVMainFrame.getInstance().repaintDataPanels();
-                    }
-                });
+                IGVMainFrame.getInstance().getTrackManager().sortAlignmentTracks(SortOption.READ_GROUP);
+                refresh();
+
             }
         });
 
-        if (IGVModel.getInstance().getViewContext().getScale() >= MIN_ALIGNMENT_SPACING) {
+
+        if (ViewContext.getInstance().getScale() >= MIN_ALIGNMENT_SPACING) {
             item.setEnabled(false);
         }
 
@@ -629,17 +753,101 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
         menu.add(item);
     }
 
+
+    private void setColorOption(ColorOption option) {
+        colorByOption = option;
+        PreferenceManager.getInstance().put(PreferenceManager.SAMPreferences.COLOR_BY, option.toString());
+        PreferenceManager.getInstance().updateSAMPreferences();
+    }
+
+    public void addColorByMenuItem(JPopupMenu menu) {
+        // Change track height by attribute
+        JMenu item = new JMenu("Color alignments");
+
+        ButtonGroup group = new ButtonGroup();
+
+        JRadioButtonMenuItem m1 = new JRadioButtonMenuItem("by insert size");
+        m1.setSelected(colorByOption == ColorOption.INSERT_SIZE);
+        m1.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                setColorOption(ColorOption.INSERT_SIZE);
+                refresh();
+            }
+        });
+
+        JRadioButtonMenuItem m1a = new JRadioButtonMenuItem("by pair orientation");
+        m1a.setSelected(colorByOption == ColorOption.PAIR_ORIENTATION);
+        m1a.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                setColorOption(ColorOption.PAIR_ORIENTATION);
+                refresh();
+            }
+        });
+
+        JRadioButtonMenuItem m2 = new JRadioButtonMenuItem("by read strand");
+        m2.setSelected(colorByOption == ColorOption.READ_STRAND);
+        m2.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                setColorOption(ColorOption.READ_STRAND);
+                refresh();
+            }
+        });
+
+        JRadioButtonMenuItem m3 = new JRadioButtonMenuItem("by first-in-pair read strand");
+        m3.setSelected(colorByOption == ColorOption.FRAGMENT_STRAND);
+        m3.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                setColorOption(ColorOption.FRAGMENT_STRAND);
+                refresh();
+            }
+        });
+
+        JRadioButtonMenuItem m4 = new JRadioButtonMenuItem("by read group");
+        m4.setSelected(colorByOption == ColorOption.READ_GROUP);
+        m4.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                setColorOption(ColorOption.READ_GROUP);
+                refresh();
+            }
+        });
+
+        JRadioButtonMenuItem m5 = new JRadioButtonMenuItem("by sample");
+        m5.setSelected(colorByOption == ColorOption.SAMPLE);
+        m5.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                setColorOption(ColorOption.SAMPLE);
+                refresh();
+            }
+        });
+
+        item.add(m1);
+        item.add(m1a);
+        item.add(m2);
+        item.add(m3);
+        item.add(m5);
+        item.add(m4);
+        group.add(m1);
+        group.add(m1a);
+        group.add(m2);
+        group.add(m3);
+        group.add(m5);
+        group.add(m4);
+        menu.add(item);
+
+    }
+
+
     public void addPackMenuItem(JPopupMenu menu) {
         // Change track height by attribute
         JMenuItem item = new JMenuItem("Re-pack alignments");
         item.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
+                UIUtilities.invokeOnEventThread(new Runnable() {
 
                     public void run() {
-                        TrackManager.getInstance().packAlignmentTracks();
-                        IGVMainFrame.getInstance().repaintDataPanels();
+                        IGVMainFrame.getInstance().getTrackManager().packAlignmentTracks();
+                        refresh();
                     }
                 });
             }
@@ -654,7 +862,7 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
         item.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
+                UIUtilities.invokeOnEventThread(new Runnable() {
 
                     public void run() {
                         copyToClipboard(me);
@@ -662,7 +870,7 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
                 });
             }
         });
-        if (IGVModel.getInstance().getViewContext().getScale() >= MIN_ALIGNMENT_SPACING) {
+        if (ViewContext.getInstance().getScale() >= MIN_ALIGNMENT_SPACING) {
             item.setEnabled(false);
         }
 
@@ -671,19 +879,14 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
 
     public void addGoToMate(JPopupMenu menu, final MouseEvent me) {
         // Change track height by attribute
-        JMenuItem item = new JMenuItem("Go to mate region");
+        JMenuItem item = new JMenuItem("Go to mate");
         item.addActionListener(new ActionListener() {
-
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
-
-                    public void run() {
-                        gotoMate(me);
-                    }
-                });
+                gotoMate(me);
             }
         });
 
+
         menu.add(item);
     }
 
@@ -693,11 +896,12 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
         item.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
+                UIUtilities.invokeOnEventThread(new Runnable() {
 
                     public void run() {
                         AlignmentTrack.this.selectedReadNames.clear();
-                        IGVMainFrame.getInstance().repaintDataPanels();
+                        selectionColorIndex = 0;
+                        refresh();
                     }
                 });
             }
@@ -709,15 +913,15 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
     public void addShowAllBasesMenuItem(JPopupMenu menu) {
         // Change track height by attribute
         final JMenuItem item = new JCheckBoxMenuItem("Show all bases");
-        item.setSelected(renderOptions.showAllBases);
+        item.setSelected(renderOptions.isShowAllBases());
         item.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
+                UIUtilities.invokeOnEventThread(new Runnable() {
 
                     public void run() {
-                        renderOptions.showAllBases = item.isSelected();
-                        IGVMainFrame.getInstance().repaintDataPanels();
+                        renderOptions.setShowAllBases(item.isSelected());
+                        refresh();
                     }
                 });
             }
@@ -726,70 +930,75 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
         menu.add(item);
     }
 
-    public void addInsertSizeMenuItem(JPopupMenu menu) {
+    public void addCoverageDepthMenuItem(JPopupMenu menu) {
         // Change track height by attribute
-        final JMenuItem item = new JCheckBoxMenuItem("Set insert size threshold ...");
+        final JMenuItem item = new JCheckBoxMenuItem("Set maximum coverage depth ...");
         item.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
-
-                    public void run() {
-                        int threshold = renderOptions.insertSizeThreshold;
-                        String val = JOptionPane.showInputDialog("Enter insert size threshold: ", String.valueOf(threshold));
-                        try {
-                            int newThreshold = Integer.parseInt(val);
-                            if(newThreshold != threshold) {
-                                renderOptions.insertSizeThreshold = newThreshold;
-                                IGVMainFrame.getInstance().repaintDataPanels();
-                            }
-                        }
-                        catch(NumberFormatException e) {
-                            MessageUtils.showMessage("Insert size must be an integer value: " + val);
-                        }
-
-
+                int maxLevels = dataManager.getMaxLevels();
+                String val = MessageUtils.showInputDialog("Maximum coverage depth", String.valueOf(maxLevels));
+                try {
+                    int newMaxLevels = Integer.parseInt(val);
+                    if (newMaxLevels != maxLevels) {
+                        dataManager.setMaxLevels(newMaxLevels);
+                        dataManager.reload();
+                        refresh();
                     }
-                });
+                }
+                catch (NumberFormatException ex) {
+                    MessageUtils.showMessage("Insert size must be an integer value: " + val);
+                }
+
             }
         });
 
+
         menu.add(item);
     }
 
-    public void addShadeBaseMenuItem(JPopupMenu menu) {
+    public void addInsertSizeMenuItem(JPopupMenu menu) {
         // Change track height by attribute
-        final JMenuItem item = new JCheckBoxMenuItem("Shade base by quality");
-        item.setSelected(renderOptions.shadeBases);
+        final JMenuItem item = new JCheckBoxMenuItem("Set insert size threshold ...");
         item.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
-
-                    public void run() {
-                        renderOptions.shadeBases = item.isSelected();
-                        IGVMainFrame.getInstance().repaintDataPanels();
+                int threshold = renderOptions.getInsertSizeThreshold();
+                String val = MessageUtils.showInputDialog("insert size threshold", String.valueOf(threshold));
+                try {
+                    int newThreshold = Integer.parseInt(val);
+                    if (newThreshold != threshold) {
+                        renderOptions.setInsertSizeThreshold(newThreshold);
+                        refresh();
                     }
-                });
+                }
+                catch (NumberFormatException ex) {
+                    MessageUtils.showMessage("Insert size must be an integer value: " + val);
+                }
+
             }
         });
 
+
         menu.add(item);
     }
 
-    public void addShadeCentersMenuItem(JPopupMenu menu) {
+    private void refresh() {
+        IGVMainFrame.getInstance().repaintDataPanels();
+    }
+
+    public void addShadeBaseMenuItem(JPopupMenu menu) {
         // Change track height by attribute
-        final JMenuItem item = new JCheckBoxMenuItem("Shade alignments intersecting center");
-        item.setSelected(renderOptions.shadeCenters);
+        final JMenuItem item = new JCheckBoxMenuItem("Shade base by quality");
+        item.setSelected(renderOptions.isShadeBases());
         item.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
+                UIUtilities.invokeOnEventThread(new Runnable() {
 
                     public void run() {
-
-                        renderOptions.shadeCenters = item.isSelected();
-                        IGVMainFrame.getInstance().repaintDataPanels();
+                        renderOptions.setShadeBases(item.isSelected());
+                        refresh();
                     }
                 });
             }
@@ -798,18 +1007,19 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
         menu.add(item);
     }
 
-    public void addColorByStrandMenuItem(JPopupMenu menu) {
+    public void addShadeCentersMenuItem(JPopupMenu menu) {
         // Change track height by attribute
-        final JMenuItem item = new JCheckBoxMenuItem("Color by strand");
-        item.setSelected(renderOptions.colorByStrand);
+        final JMenuItem item = new JCheckBoxMenuItem("Shade alignments intersecting center");
+        item.setSelected(renderOptions.isShadeCenters());
         item.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
+                UIUtilities.invokeOnEventThread(new Runnable() {
 
                     public void run() {
-                        renderOptions.colorByStrand = item.isSelected();
-                        IGVMainFrame.getInstance().repaintDataPanels();
+
+                        renderOptions.setShadeCenters(item.isSelected());
+                        refresh();
                     }
                 });
             }
@@ -818,19 +1028,20 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
         menu.add(item);
     }
 
+
     public void addShowCoverageItem(JPopupMenu menu) {
         // Change track height by attribute
         final JMenuItem item = new JCheckBoxMenuItem("Show coverage track");
-        item.setSelected(coverageTrack != null && coverageTrack.isVisible());
+        item.setSelected(getCoverageTrack() != null && getCoverageTrack().isVisible());
         item.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
+                UIUtilities.invokeOnEventThread(new Runnable() {
 
                     public void run() {
-                        if (coverageTrack != null) {
-                            coverageTrack.setVisible(item.isSelected());
-                            IGVMainFrame.getInstance().repaintDataPanels();
+                        if (getCoverageTrack() != null) {
+                            getCoverageTrack().setVisible(item.isSelected());
+                            refresh();
                             IGVMainFrame.getInstance().repaintNamePanels();
                         }
                     }
@@ -847,7 +1058,7 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
         item.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
+                UIUtilities.invokeOnEventThread(new Runnable() {
 
                     public void run() {
 
@@ -860,9 +1071,9 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
                             if (path.endsWith(".tdf") || path.endsWith(".tdf")) {
 
                                 TDFReader reader = TDFReader.getReader(file.getAbsolutePath());
-                                TDFDataSource ds = new TDFDataSource(reader, 0, getDisplayName() + " coverage");
-                                coverageTrack.setDataSource(ds);
-                                IGVMainFrame.getInstance().repaintDataPanels();
+                                TDFDataSource ds = new TDFDataSource(reader, 0, getName() + " coverage");
+                                getCoverageTrack().setDataSource(ds);
+                                refresh();
                             } else {
                                 MessageUtils.showMessage("Coverage data must be in .tdf format");
                             }
@@ -876,14 +1087,15 @@ public class AlignmentTrack extends AbstractTrack implements DragListener {
     }
 
     @Override
-    public Map<String, String> getSessionAttributes() {
-        Map<String, String> attrs = super.getSessionAttributes();
-
+    public Map<String, String> getPersistentState() {
+        Map<String, String> attrs = super.getPersistentState();
+        attrs.putAll(renderOptions.getPersistentState());
         return attrs;
     }
 
     @Override
-    public void update(Map<String, String> attributes) {
-        super.update(attributes);    //To change body of overridden methods use File | Settings | File Templates.
+    public void restorePersistentState(Map<String, String> attributes) {
+        super.restorePersistentState(attributes);    //To change body of overridden methods use File | Settings | File Templates.
+        renderOptions.restorePersistentState(attributes);
     }
 }
diff --git a/src/org/broad/igv/sam/BedRenderer.java b/src/org/broad/igv/sam/BedRenderer.java
index ec8ca29..2329a5f 100644
--- a/src/org/broad/igv/sam/BedRenderer.java
+++ b/src/org/broad/igv/sam/BedRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -34,7 +34,7 @@ import java.util.Map;
 public class BedRenderer implements FeatureRenderer {
 
     public void renderAlignments(List<Alignment> alignments, RenderContext context,
-                                 Rectangle rect, AlignmentTrack.RenderOptions renderOptions, boolean leaveMargin, 
+                                 Rectangle rect, AlignmentTrack.RenderOptions renderOptions, boolean leaveMargin,
                                  Map<String, Color> selectedReadNames) {
 
         double origin = context.getOrigin();
diff --git a/src/org/broad/igv/sam/CoverageTrack.java b/src/org/broad/igv/sam/CoverageTrack.java
index a7fe90d..ecc6c0d 100644
--- a/src/org/broad/igv/sam/CoverageTrack.java
+++ b/src/org/broad/igv/sam/CoverageTrack.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,17 +23,25 @@
 package org.broad.igv.sam;
 
 import com.jidesoft.swing.JidePopupMenu;
+import org.apache.log4j.Logger;
+import org.broad.igv.PreferenceManager;
+import org.broad.igv.sam.reader.CachingQueryReader;
+import org.broad.igv.util.ColorUtilities;
+import org.broad.igv.util.AsciiLineReader;
+import org.broad.igv.util.ParsingUtils;
+import org.broad.igv.util.ResourceLocator;
+import org.broad.igv.feature.FeatureUtils;
 import org.broad.igv.feature.LocusScore;
-import org.broad.igv.tdf.TDFDataSource;
-import org.broad.igv.tdf.TDFReader;
 import org.broad.igv.renderer.BarChartRenderer;
 import org.broad.igv.renderer.DataRange;
 import org.broad.igv.renderer.DataRenderer;
 import org.broad.igv.renderer.Renderer;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.tdf.TDFDataSource;
+import org.broad.igv.tdf.TDFReader;
 import org.broad.igv.track.*;
 import org.broad.igv.ui.DataRangeDialog;
 import org.broad.igv.ui.FontManager;
-import org.broad.igv.ui.GuiUtilities;
 import org.broad.igv.ui.IGVMainFrame;
 import org.broad.igv.ui.util.FileChooserDialog;
 import org.broad.igv.ui.util.MessageUtils;
@@ -44,34 +52,57 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.MouseEvent;
 import java.io.File;
-import java.util.ArrayList;
-import java.util.Collection;
+import java.util.*;
 import java.util.List;
-import java.util.Map;
 
 /**
  * @author jrobinso
  */
 public class CoverageTrack extends AbstractTrack {
 
+    private static Logger log = Logger.getLogger(CoverageTrack.class);
+    private static Map<String, Set<Integer>> knownSnps;
+
     char[] nucleotides = {'a', 'c', 'g', 't', 'n'};
     public static Color lightBlue = new Color(0, 0, 150);
-    boolean showReference;
-    boolean autoScale = true;
+    private float[] bgColorComps = new float[3];
+    private static final boolean DEFAULT_AUTOSCALE = true;
+    private static final boolean DEFAULT_SHOW_REFERENCE = false;
 
+    // User settable state -- these attributes should be stored in the session file
+    boolean showReference;
+    boolean autoScale = DEFAULT_AUTOSCALE;
+    private float snpThreshold;
+    private boolean showAllSnps;
 
     AlignmentDataManager dataManager;
-
     TDFDataSource dataSource;
     DataRenderer dataSourceRenderer; // = new BarChartRenderer();
-
     IntervalRenderer intervalRenderer;
+    PreferenceManager prefs;
+    JMenuItem dataRangeItem;
+    JMenuItem autoscaleItem;
+
 
     public CoverageTrack(String id, String name) {
         super(id, name);
         super.setDataRange(new DataRange(0, 0, 60));
         intervalRenderer = new IntervalRenderer();
+
         setColor(AlignmentRenderer.grey1);
+        AlignmentRenderer.grey1.getColorComponents(bgColorComps);
+
+        prefs = PreferenceManager.getInstance();
+        snpThreshold = prefs.getSAMPreferences().getAlleleFreqThreshold();
+        autoScale = DEFAULT_AUTOSCALE;
+        showReference = DEFAULT_SHOW_REFERENCE;
+        showAllSnps = prefs.getBooleanPreference("COVERAGE.SHOW_ALL_MISMATCHES", DEFAULT_SHOW_REFERENCE);
+        //TODO  logScale = prefs.
+
+        String snpsFile = prefs.get(PreferenceManager.KNOWN_SNPS, null);
+        if (snpsFile != null && knownSnps == null) {
+            loadKnownSnps(snpsFile);
+        }
     }
 
     public void setDataManager(AlignmentDataManager dataManager) {
@@ -81,15 +112,16 @@ public class CoverageTrack extends AbstractTrack {
     public void setDataSource(TDFDataSource dataSource) {
         this.dataSource = dataSource;
         dataSourceRenderer = new BarChartRenderer();
-        setDataRange(new DataRange(0, 0, 1.5f * (float) dataSource.getDataMax(null)));
+        setDataRange(new DataRange(0, 0, 1.5f * (float) dataSource.getDataMax()));
 
     }
 
 
     @Override
     public void setDataRange(DataRange axisDefinition) {
+        // Explicitly setting a data range turns off auto-scale
         autoScale = false;
-//        autoscaleItem.setEnabled(!autoScale);
+
         super.setDataRange(axisDefinition);
     }
 
@@ -98,28 +130,33 @@ public class CoverageTrack extends AbstractTrack {
             AlignmentInterval interval = dataManager.getLoadedInterval();
             if (interval != null) {
                 int max = Math.max(10, interval.getMaxCount());
+                DataRange.Type type = getDataRange().getType();
                 super.setDataRange(new DataRange(0, 0, max));
+                getDataRange().setType(type);
             }
         }
     }
 
+
     public void render(RenderContext context, Rectangle rect) {
 
-        AlignmentInterval interval = null;
-        if (dataManager != null) {
-            String chr = context.getChr();
-            int start = (int) context.getStartLocation();
-            int end = (int) context.getEndLocation();
-            interval = dataManager.getInterval(chr, start, end);
-        }
-        if (interval != null) {
-            intervalRenderer.paint(context, rect, interval, getDataRange().getMaximum());
-        }
+        float maxRange = PreferenceManager.getInstance().getSAMPreferences().getMaxVisibleRange();
+        float minVisibleScale = (maxRange * 1000) / 700;
+
+        if (context.getScale() < minVisibleScale) {
+            AlignmentInterval interval = null;
+            if (dataManager != null) {
+                interval = dataManager.getLoadedInterval();
+            }
 
-        // Use precomputed data source, if any
-        else if (dataSource != null) {
+            if (interval != null && interval.overlaps(context.getGenomeId(), context.getChr(), (int) context.getOrigin(),
+                    (int) context.getEndLocation())) {
+                List<CachingQueryReader.AlignmentCounts> counts = interval.counts;
+                intervalRenderer.paint(context, rect, counts);
+            }
+        } else if (dataSource != null) {
             String chr = context.getChr();
-            int start = (int) context.getStartLocation();
+            int start = (int) context.getOrigin();
             int end = (int) context.getEndLocation();
             int zoom = context.getZoom();
             List<LocusScore> scores = dataSource.getSummaryScoresForRange(chr, start, end, zoom);
@@ -129,6 +166,12 @@ public class CoverageTrack extends AbstractTrack {
 
         }
 
+        drawBorder(context, rect);
+
+
+    }
+
+    private void drawBorder(RenderContext context, Rectangle rect) {
         // Draw border
         context.getGraphic2DForColor(Color.gray).drawLine(
                 rect.x, rect.y + rect.height,
@@ -144,7 +187,7 @@ public class CoverageTrack extends AbstractTrack {
                 g.setFont(smallFont);
                 String scale = "[" + (int) range.getMinimum() + " - " +
                         (int) range.getMaximum() + "]";
-                g.drawString(scale, rect.x + 10, rect.y + rect.height - 10);
+                g.drawString(scale, rect.x + 5, rect.y + 10);
 
             } finally {
                 g.setFont(font);
@@ -173,8 +216,8 @@ public class CoverageTrack extends AbstractTrack {
 
     public String getValueStringAt(String chr, double position, int y) {
         AlignmentInterval interval = dataManager.getLoadedInterval();
-        if (interval == null) {
-            return "";
+        if (interval == null || !interval.contains(chr, (int) position, (int) position)) {
+            return getPrecomputedValueString(chr, position);
         } else {
             StringBuffer buf = new StringBuffer();
             final int pos = (int) position - 1;
@@ -198,64 +241,78 @@ public class CoverageTrack extends AbstractTrack {
 
     }
 
-    public float getRegionScore(String chr, int start, int end, int zoom, RegionScoreType type) {
-        return 0;
-    }
-
-
-    class IntervalRenderer {
+    private String getPrecomputedValueString(String chr, double position) {
 
-        private void paint(RenderContext context, Rectangle rect, AlignmentInterval interval, final double dataMax) {
+        if (dataSource == null) {
+            return "";
+        }
+        int zoom = Math.max(0, ViewContext.getInstance().getZoom());
+        List<LocusScore> scores = dataSource.getSummaryScoresForRange(chr, (int) position - 10, (int) position + 10, zoom);
 
-            Graphics2D graphics = context.getGraphic2DForColor(AlignmentRenderer.grey1);
+        // give a 2 pixel window, otherwise very narrow features will be missed.
+        double bpPerPixel = ViewContext.getInstance().getScale();
+        double minWidth = 2 * bpPerPixel;    /* * */
 
-            DataRange range = getDataRange();
+        if (scores == null) {
+            return "";
+        } else {
+            LocusScore score = FeatureUtils.getFeatureAt(position, minWidth, scores);
+            return score == null ? "" : "Mean count: " + score.getScore();
+        }
+    }
 
+    public float getRegionScore(String chr, int start, int end, int zoom, RegionScoreType type) {
+        return 0;
+    }
 
-            // Temporary until proper windowing is implemented
-            int lastpX = -1;
 
-            for (int pos = interval.getStart(); pos < interval.getEnd(); pos++) {
+    /**
+     * Load the set of known snps from a tab delimited file, format
+     * chr < tab> location
+     * The location is "1 base"  (first nucleotide is position 1).
+     *
+     * @param snpFile
+     */
+    private static synchronized void loadKnownSnps(String snpFile) {
 
-                int pX = (int) (rect.getX() + (pos - context.getOrigin()) / context.getScale());
-                int dX = Math.max(1,
-                        (int) (rect.getX() + (pos + 1 - context.getOrigin()) / context.getScale()) - pX);
-                if (dX > 3) {
-                    dX--;
-                }
+        // This method might get called many times concurrently, but we only want to load these once.
+        if (knownSnps != null) {
+            return;
+        }
 
-                if (pX > rect.getMaxX()) {
-                    break;
+        knownSnps = new HashMap();
+        AsciiLineReader reader = null;
+        try {
+            reader = ParsingUtils.openAsciiReader(new ResourceLocator(snpFile));
+            String nextLine = "";
+            while ((nextLine = reader.readLine()) != null) {
+                String[] tokens = nextLine.split("\t");
+                String chr = tokens[0];
+                Set<Integer> snps = knownSnps.get(chr);
+                if (snps == null) {
+                    snps = new HashSet(10000);
+                    knownSnps.put(chr, snps);
                 }
+                snps.add(new Integer(tokens[1]));
+            }
+        } catch (Exception e) {
+            knownSnps = null;
+            log.error("", e);
+            MessageUtils.showMessage("Error loading snps file: " + snpFile + " (" + e.toString() + ")");
+        } finally {
+            reader.close();
+        }
 
-                if (pX + dX >= 0) {
-
-                    boolean plusMinusOption = false;
-                    boolean nonRefOption = false;
-
-
-                    // Test to see if any single nucleotide mismatch  (nucleotide other than the reference)
-                    // has posA quality weight > 20% of the total
-                    boolean colorBases = false;
-                    float threshold = 0.2f * interval.getTotalQuality(pos);
-                    char ref = Character.toLowerCase((char) interval.getReference(pos));
-                    if (ref > 0) {
-                        for (char c : nucleotides) {
-                            if (c != ref && c != 'n' && interval.getQuality(pos, (byte) c) > threshold) {
-                                colorBases = true;
-                                break;
-                            }
-                        }
-                    }
 
-                    if (pX > lastpX || colorBases) {
+    }
 
-                        if (plusMinusOption || nonRefOption) {
+    /*
+                           if (plusMinusOption || nonRefOption) {
 
                             int pY = (int) (rect.getY() + rect.getMaxY()) / 2;
 
                             int totalNegCount = interval.getNegTotal(pos);
-                            int height = (int) (totalNegCount * rect.getHeight() / range.getMaximum());
+                            int height = (int) (totalNegCount * rect.getHeight() / getColorScale().getMaximum());
                             height = Math.min(height, rect.height / 2 - 1);
                             if (height > 0) {
 
@@ -265,7 +322,7 @@ public class CoverageTrack extends AbstractTrack {
                                     if (colorBases) {
                                         for (char c : nucleotides) {
                                             if (nonRefOption == false || c != ref) {
-                                                pY = drawBar(context, pos, rect, range.getMaximum(), pY, pX, dX, c,
+                                                pY = drawBar(context, pos, rect, getColorScale().getMaximum(), pY, pX, dX, c,
                                                         plusMinusOption || nonRefOption, false, interval);
                                             }
                                         }
@@ -277,7 +334,7 @@ public class CoverageTrack extends AbstractTrack {
                             pY = (int) (rect.getY() + rect.getMaxY()) / 2;
                             int totalPosCount = interval.getPosTotal(pos);
 
-                            height = (int) (totalPosCount * rect.getHeight() / range.getMaximum());
+                            height = (int) (totalPosCount * rect.getHeight() / getColorScale().getMaximum());
                             height = Math.min(height, rect.height / 2 - 1);
                             int topY = (pY - height);
 
@@ -290,7 +347,7 @@ public class CoverageTrack extends AbstractTrack {
                                 if (colorBases) {
                                     for (char c : nucleotides) {
                                         if (nonRefOption == false || c != ref) {
-                                            pY = drawBar(context, pos, rect, range.getMaximum(), pY, pX, dX, c,
+                                            pY = drawBar(context, pos, rect, getColorScale().getMaximum(), pY, pX, dX, c,
                                                     plusMinusOption || nonRefOption, true, interval);
                                         }
                                     }
@@ -307,7 +364,7 @@ public class CoverageTrack extends AbstractTrack {
                             int totalCount = interval.getTotalCount(pos);
 
                             int pY = (int) rect.getMaxY() - 1;
-                            int height = (int) (totalCount * rect.getHeight() / range.getMaximum());
+                            int height = (int) (totalCount * rect.getHeight() / getColorScale().getMaximum());
                             height = Math.min(height, rect.height - 1);
                             int topY = (pY - height);
 
@@ -321,84 +378,322 @@ public class CoverageTrack extends AbstractTrack {
                                 }
                             }
                         }
+     */
+
+    class IntervalRenderer {
 
-                        lastpX = pX;
+
+        private void paint(RenderContext context, Rectangle rect, List<CachingQueryReader.AlignmentCounts> countList) {
+
+            Graphics2D graphics = context.getGraphic2DForColor(AlignmentRenderer.grey1);
+
+            DataRange range = getDataRange();
+            double max = range.isLog() ? Math.log10(range.getMaximum()) : range.getMaximum();
+
+            // Temporary until proper windowing is implemented
+            int lastpX = -1;
+
+            for (CachingQueryReader.AlignmentCounts alignmentCounts : countList) {
+
+                for (int pos = alignmentCounts.getStart(); pos < alignmentCounts.getEnd(); pos++) {
+
+                    int pX = (int) (rect.getX() + (pos - context.getOrigin()) / context.getScale());
+                    int dX = Math.max(1,
+                            (int) (rect.getX() + (pos + 1 - context.getOrigin()) / context.getScale()) - pX);
+                    if (dX > 3) {
+                        dX--;
                     }
 
-                }
-            }
+                    if (pX > rect.getMaxX()) {
+                        break;
+                    }
+
+                    if (pX + dX >= 0) {
+
+                        // Test to see if any single nucleotide mismatch  (nucleotide other than the reference)
+                        // has posA quality weight > 20% of the total
+                        // Skip this test if the position is in the list of known snps
+                        char ref = Character.toLowerCase((char) alignmentCounts.getReference(pos));
+                        boolean mismatch = false;
+
+                        Set<Integer> snps = knownSnps == null ? null : knownSnps.get(context.getChr());
+                        if (snps == null || !snps.contains(pos + 1)) {
+                            float threshold = snpThreshold * alignmentCounts.getTotalQuality(pos);
+                            if (ref > 0) {
+                                for (char c : nucleotides) {
+                                    if (c != ref && c != 'n' && alignmentCounts.getQuality(pos, (byte) c) > threshold) {
+                                        mismatch = true;
+                                        break;
+                                    }
+                                }
+                            }
+                        }
+
+                        if (pX > lastpX || mismatch) {
+
+                            boolean strandOption = false;
+
+                            if (strandOption) {
+
+                                int pY = (int) (rect.getY() + rect.getMaxY()) / 2;
+
+                                int totalNegCount = alignmentCounts.getNegTotal(pos);
+                                int height = (int) (totalNegCount * rect.getHeight() / getColorScale().getMaximum());
+                                height = Math.min(height, rect.height / 2 - 1);
+                                if (height > 0) {
 
+                                    graphics.fillRect(pX, pY, dX, height);
+
+                                    if (mismatch || showAllSnps) {
+                                        for (char c : nucleotides) {
+                                            if (c != ref) {
+                                                pY = drawStrandBar(context, pos, rect, getColorScale().getMaximum(), pY, pX, dX, c,
+                                                        false, alignmentCounts);
+                                            }
+                                        }
+                                    }
 
+                                }
+
+                                pY = (int) (rect.getY() + rect.getMaxY()) / 2;
+                                int totalPosCount = alignmentCounts.getPosTotal(pos);
+
+                                height = (int) (totalPosCount * rect.getHeight() / getColorScale().getMaximum());
+                                height = Math.min(height, rect.height / 2 - 1);
+                                int topY = (pY - height);
+
+                                if (height > 0) {
+
+                                    graphics.fillRect(pX, topY, dX, height);
+
+                                    if (mismatch || showAllSnps) {
+                                        for (char c : nucleotides) {
+                                            if (c != ref) {
+                                                pY = drawStrandBar(context, pos, rect, getColorScale().getMaximum(), pY, pX, dX, c,
+                                                        true, alignmentCounts);
+                                            }
+                                        }
+                                    }
+                                }
+
+                                pY = (int) (rect.getY() + rect.getMaxY()) / 2;
+                                Graphics2D blackGraphics = context.getGraphic2DForColor(lightBlue);
+                                blackGraphics.drawLine(0, pY, rect.width, pY);
+                            } else {
+
+                                int pY = (int) rect.getMaxY() - 1;
+
+                                int totalCount = alignmentCounts.getTotalCount(pos);
+
+                                double tmp = range.isLog() ? Math.log10(totalCount) / max : totalCount / max;
+                                int height = (int) (tmp * rect.getHeight());
+
+                                height = Math.min(height, rect.height - 1);
+                                int topY = (pY - height);
+
+                                if (height > 0) {
+                                    graphics.fillRect(pX, topY, dX, height);
+
+                                    if (mismatch || showAllSnps) {
+                                        for (char c : nucleotides) {
+                                            if (!(c == ref && showAllSnps)) {
+                                                pY = drawBar(context, pos, rect, totalCount, max,
+                                                        pY, pX, dX, c, alignmentCounts, range.isLog());
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                            lastpX = pX;
+                        }
+                    }
+                }
+            }
         }
 
+        /**
+         * Draw a colored bar to represent a mismatch to the reference.   The height is proportional to the % of
+         * reads with respect to the total.  If "showAllSnps == true"  the bar is shaded by avg read quality.
+         *
+         * @param context
+         * @param pos
+         * @param rect
+         * @param max
+         * @param pY
+         * @param pX
+         * @param dX
+         * @param nucleotide
+         * @param interval
+         * @return
+         */
+
         int drawBar(RenderContext context,
                     int pos,
                     Rectangle rect,
-                    double maxCount,
+                    double totalCount,
+                    double max,
                     int pY,
                     int pX,
                     int dX,
                     char nucleotide,
-                    boolean negPosOption,
-                    boolean isPositive,
-                    AlignmentInterval interval) {
+                    CachingQueryReader.AlignmentCounts interval,
+                    boolean isLog) {
+
+            int count = interval.getCount(pos, (byte) nucleotide);
 
+            Color c = AlignmentRenderer.getNucleotideColors().get(nucleotide);
+
+            if (showAllSnps) {
+                int q = interval.getAvgQuality(pos, (byte) nucleotide);
+                c = getShadedColor(q, c);
+            }
 
-            Color c = AlignmentRenderer.nucleotideColors.get(nucleotide);
             Graphics2D tGraphics = context.getGraphic2DForColor(c);
 
-            if (negPosOption == false) {
-                int count = interval.getCount(pos, (byte) nucleotide);
-                int height = (int) Math.round(count * rect.getHeight() / maxCount);
-                height = Math.min(pY - rect.y, height);
-                int baseY = (int) (pY - height);
+            double tmp = isLog ?
+                    (count / totalCount) * Math.log10(totalCount) / max :
+                    count / max;
+            int height = (int) (tmp * rect.getHeight());
 
-                if (height > 0) {
-                    tGraphics.fillRect(pX, baseY, dX, height);
-                }
-                return baseY;
-            } else {
+            height = Math.min(pY - rect.y, height);
+            int baseY = pY - height;
 
-                int count = isPositive ? interval.getPosCount(pos, (byte) nucleotide) :
-                        interval.getNegCount(pos, (byte) nucleotide);
+            if (height > 0) {
+                tGraphics.fillRect(pX, baseY, dX, height);
+            }
+            return baseY;
+        }
 
-                int height = (int) Math.round(count * rect.getHeight() / maxCount);
-                height = isPositive ? Math.min(pY - rect.y, height) :
-                        Math.min(rect.y + rect.height - pY, height);
-                int baseY = (int) (isPositive ? (pY - height) : pY);
 
-                if (height > 0) {
-                    tGraphics.fillRect(pX, baseY, dX, height);
-                }
-                return isPositive ? baseY : baseY + height;
+        /**
+         * Strand-specific
+         *
+         * @param context
+         * @param pos
+         * @param rect
+         * @param maxCount
+         * @param pY
+         * @param pX
+         * @param dX
+         * @param nucleotide
+         * @param isPositive
+         * @param interval
+         * @return
+         */
+        int drawStrandBar(RenderContext context,
+                          int pos,
+                          Rectangle rect,
+                          double maxCount,
+                          int pY,
+                          int pX,
+                          int dX,
+                          char nucleotide,
+                          boolean isPositive,
+                          CachingQueryReader.AlignmentCounts interval) {
+
+
+            Color c = AlignmentRenderer.getNucleotideColors().get(nucleotide);
+            Graphics2D tGraphics = context.getGraphic2DForColor(c);
+
+
+            int count = isPositive ? interval.getPosCount(pos, (byte) nucleotide) :
+                    interval.getNegCount(pos, (byte) nucleotide);
 
+            int height = (int) Math.round(count * rect.getHeight() / maxCount);
+            height = isPositive ? Math.min(pY - rect.y, height) :
+                    Math.min(rect.y + rect.height - pY, height);
+            int baseY = (int) (isPositive ? (pY - height) : pY);
+
+            if (height > 0) {
+                tGraphics.fillRect(pX, baseY, dX, height);
             }
+            return isPositive ? baseY : baseY + height;
+        }
+
+    }
+
 
+    static float[] colorComps = new float[3];
 
+    private Color getShadedColor(int qual, Color color) {
+        float alpha = 0;
+        int minQ = prefs.getSAMPreferences().getBaseQualityMin();
+        color.getRGBColorComponents(colorComps);
+        if (qual < minQ) {
+            alpha = 0.1f;
+        } else {
+            int maxQ = prefs.getSAMPreferences().getBaseQualityMax();
+            alpha = Math.max(0.1f, Math.min(1.0f, 0.1f + 0.9f * (qual - minQ) / (maxQ - minQ)));
         }
 
+        // Round alpha to nearest 0.1, for effeciency;
+        alpha = ((int) (alpha * 10 + 0.5f)) / 10.0f;
+        color = ColorUtilities.getCompositeColor(bgColorComps, colorComps, alpha);
+        return color;
     }
 
 
+    /**
+     * Called by session writer.  Return instance variable values as a map of strings.  Used to record current state
+     * of object.   Variables with default values are not stored, as it is presumed the user has not changed them.
+     *
+     * @return
+     */
     @Override
-    public Map<String, String> getSessionAttributes() {
-        Map<String, String> attributes = super.getSessionAttributes();
+    public Map<String, String> getPersistentState() {
+        Map<String, String> attributes = super.getPersistentState();
         if (dataSource != null) {
             attributes.put("path", dataSource.getPath());
         }
+        prefs = PreferenceManager.getInstance();
+        if (snpThreshold != prefs.getSAMPreferences().getAlleleFreqThreshold()) {
+            attributes.put("snpThreshold", String.valueOf(snpThreshold));
+        }
+        if (autoScale != DEFAULT_AUTOSCALE) {
+            attributes.put("autoScale", String.valueOf(autoScale));
+        }
+        if (showReference != DEFAULT_SHOW_REFERENCE) {
+            attributes.put("showReference", String.valueOf(showReference));
+        }
+        if (showAllSnps != prefs.getBooleanPreference("COVERAGE.SHOW_ALL_MISMATCHES", false)) {
+            attributes.put("showAllSnps", String.valueOf(showAllSnps));
+        }
+
         return attributes;
     }
 
+    /**
+     * Called by session reader.  Restores state of object.
+     *
+     * @param attributes
+     */
     @Override
-    public void update(Map<String, String> attributes) {
-        super.update(attributes);    //To change body of overridden methods use File | Settings | File Templates.
+    public void restorePersistentState(Map<String, String> attributes) {
+        super.restorePersistentState(attributes);    //To change body of overridden methods use File | Settings | File Templates.
 
-        String path = attributes.get("path");
-        if (path != null) {
-            TDFReader reader = TDFReader.getReader(path);
+        String value;
+        value = attributes.get("path");
+        if (value != null) {
+            TDFReader reader = TDFReader.getReader(value);
             TDFDataSource ds = new TDFDataSource(reader, 0, "");
             setDataSource(ds);
         }
+        value = attributes.get("snpThreshold");
+        if (value != null) {
+            snpThreshold = Float.parseFloat(value);
+        }
+        value = attributes.get("autoScale");
+        if (value != null) {
+            autoScale = Boolean.parseBoolean(value);
+        }
+        value = attributes.get("showReference");
+        if (value != null) {
+            showReference = Boolean.parseBoolean(value);
+        }
+        value = attributes.get("showAllSnps");
+        if (value != null) {
+            showAllSnps = Boolean.parseBoolean(value);
+        }
 
     }
 
@@ -421,7 +716,7 @@ public class CoverageTrack extends AbstractTrack {
 
         JPopupMenu popupMenu = new JidePopupMenu();
 
-        JLabel popupTitle = new JLabel("  " + getDisplayName(), JLabel.CENTER);
+        JLabel popupTitle = new JLabel("  " + getName(), JLabel.CENTER);
 
         Font newFont = popupMenu.getFont().deriveFont(Font.BOLD, 12);
         popupTitle.setFont(newFont);
@@ -445,30 +740,29 @@ public class CoverageTrack extends AbstractTrack {
 
         //popupMenu.add(trackSettingsHeading);
 
-        TrackMenuUtils.addTrackRenameItem(popupMenu);
-
         ArrayList<Track> tmp = new ArrayList();
         tmp.add(this);
+        popupMenu.add(TrackMenuUtils.getTrackRenameItem(tmp));
+
 
-        addSAutscaleMenuItem(popupMenu);
-        dataRangeItem = addDataRangeItem(popupMenu, tmp, true);
+        addAutoscaleItem(popupMenu);
+        addLogScaleItem(popupMenu);
+        dataRangeItem = addDataRangeItem(popupMenu, tmp);
         dataRangeItem.setEnabled(!autoScale);
 
+        this.addSnpTresholdItem(popupMenu);
+
         popupMenu.addSeparator();
         addLoadCoverageDataItem(popupMenu);
         popupMenu.addSeparator();
 
-
-        TrackMenuUtils.addRemoveMenuItem(popupMenu);
+        popupMenu.add(TrackMenuUtils.getRemoveMenuItem(tmp));
 
         return popupMenu;
     }
 
-    JMenuItem dataRangeItem;
-    JMenuItem autoscaleItem;
 
-    public static JMenuItem addDataRangeItem(JPopupMenu menu, final Collection<Track> selectedTracks,
-                                             boolean hideMid) {
+    public JMenuItem addDataRangeItem(JPopupMenu menu, final Collection<Track> selectedTracks) {
         JMenuItem maxValItem = new JMenuItem("Set Data Range");
 
         maxValItem.addActionListener(new ActionListener() {
@@ -483,15 +777,18 @@ public class CoverageTrack extends AbstractTrack {
                     dlg.setHideMid(true);
                     dlg.setVisible(true);
                     if (!dlg.isCanceled()) {
-                        DataRange axisDefinition = new DataRange(dlg.getMin(),
-                                dlg.getBase(),
-                                dlg.getMax());
+                        float min = Math.min(dlg.getMin(), dlg.getMax());
+                        float max = Math.max(dlg.getMin(), dlg.getMax());
+                        float mid = dlg.getBase();
+                        if (mid < min) mid = min;
+                        else if (mid > max) mid = max;
+                        DataRange dataRange = new DataRange(min, mid, max);
+                        dataRange.setType(getDataRange().getType());
 
                         // dlg.isFlipAxis());
                         for (Track track : selectedTracks) {
-                            track.setDataRange(axisDefinition);
+                            track.setDataRange(dataRange);
                         }
-                        IGVMainFrame.getInstance().clearImageCacheWithNoRepaint();
                         IGVMainFrame.getInstance().repaint();
                     }
                 }
@@ -503,62 +800,103 @@ public class CoverageTrack extends AbstractTrack {
         return maxValItem;
     }
 
+    public JMenuItem addSnpTresholdItem(JPopupMenu menu) {
+        JMenuItem maxValItem = new JMenuItem("Set allele frequency threshold...");
+
+        maxValItem.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+
+                String value = JOptionPane.showInputDialog("Allele frequency threshold: ", Float.valueOf(snpThreshold));
+                if (value == null) {
+                    return;
+                }
+                try {
+                    float tmp = Float.parseFloat(value);
+                    snpThreshold = tmp;
+                    IGVMainFrame.getInstance().repaintDataPanels();
+                }
+                catch (Exception exc) {
+                    //log
+                }
+
+            }
+        });
+        menu.add(maxValItem);
+
+        return maxValItem;
+    }
+
     public void addLoadCoverageDataItem(JPopupMenu menu) {
         // Change track height by attribute
         final JMenuItem item = new JCheckBoxMenuItem("Load coverage data...");
         item.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
-
-                    public void run() {
-
-                        FileChooserDialog trackFileDialog = IGVMainFrame.getInstance().getTrackFileChooser();
-                        trackFileDialog.setMultiSelectionEnabled(false);
-                        trackFileDialog.setVisible(true);
-                        if (!trackFileDialog.isCanceled()) {
-                            File file = trackFileDialog.getSelectedFile();
-                            String path = file.getAbsolutePath();
-                            if (path.endsWith(".tdf") || path.endsWith(".tdf")) {
-
-                                TDFReader reader = TDFReader.getReader(file.getAbsolutePath());
-                                TDFDataSource ds = new TDFDataSource(reader, 0, getDisplayName() + " coverage");
-                                setDataSource(ds);
-                                IGVMainFrame.getInstance().repaintDataPanels();
-                            } else {
-                                MessageUtils.showMessage("Coverage data must be in .tdf format");
-                            }
-                        }
+
+
+                FileChooserDialog trackFileDialog = IGVMainFrame.getInstance().getTrackFileChooser();
+                trackFileDialog.setMultiSelectionEnabled(false);
+                trackFileDialog.setVisible(true);
+                if (!trackFileDialog.isCanceled()) {
+                    File file = trackFileDialog.getSelectedFile();
+                    String path = file.getAbsolutePath();
+                    if (path.endsWith(".tdf") || path.endsWith(".tdf")) {
+
+                        TDFReader reader = TDFReader.getReader(file.getAbsolutePath());
+                        TDFDataSource ds = new TDFDataSource(reader, 0, getName() + " coverage");
+                        setDataSource(ds);
+                        IGVMainFrame.getInstance().repaintDataPanels();
+                    } else {
+                        MessageUtils.showMessage("Coverage data must be in .tdf format");
                     }
-                });
+                }
             }
         });
 
         menu.add(item);
     }
 
-    public void addSAutscaleMenuItem(JPopupMenu menu) {
+    public void addAutoscaleItem(JPopupMenu menu) {
         // Change track height by attribute
         autoscaleItem = new JCheckBoxMenuItem("Autoscale");
         autoscaleItem.setSelected(autoScale);
         autoscaleItem.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
 
-                    public void run() {
-                        autoScale = autoscaleItem.isSelected();
-                        dataRangeItem.setEnabled(!autoScale);
-                        if (autoScale) {
-                            rescale();
-                        }
-                        IGVMainFrame.getInstance().repaintDataPanels();
-                    }
-                });
+                autoScale = autoscaleItem.isSelected();
+                dataRangeItem.setEnabled(!autoScale);
+                if (autoScale) {
+                    rescale();
+                }
+                IGVMainFrame.getInstance().repaintDataPanels();
+
             }
         });
 
         menu.add(autoscaleItem);
     }
 
+    public void addLogScaleItem(JPopupMenu menu) {
+        // Change track height by attribute
+        final DataRange dataRange = getDataRange();
+        final JCheckBoxMenuItem logScaleItem = new JCheckBoxMenuItem("Log scale");
+        final boolean logScale = dataRange.getType() == DataRange.Type.LOG;
+        logScaleItem.setSelected(logScale);
+        logScaleItem.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+
+                DataRange.Type scaleType = logScaleItem.isSelected() ?
+                        DataRange.Type.LOG :
+                        DataRange.Type.LINEAR;
+                dataRange.setType(scaleType);
+                IGVMainFrame.getInstance().repaintDataPanels();
+            }
+        });
+
+        menu.add(logScaleItem);
+    }
+
 }
diff --git a/src/org/broad/igv/sam/DotAlignedAlignment.java b/src/org/broad/igv/sam/DotAlignedAlignment.java
index 0fefd4e..f7980fd 100644
--- a/src/org/broad/igv/sam/DotAlignedAlignment.java
+++ b/src/org/broad/igv/sam/DotAlignedAlignment.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,8 +23,11 @@
 package org.broad.igv.sam;
 
 import org.broad.igv.feature.LocusScore;
+import org.broad.igv.feature.Strand;
 import org.broad.igv.track.WindowFunction;
 
+import java.awt.*;
+
 /**
  * @author jrobinso
  */
@@ -66,10 +69,34 @@ public class DotAlignedAlignment implements Alignment {
         return "";
     }
 
+    public void setMateSequence(String sequnce) {
+        // Ignore
+    }
+
+    public String getPairOrientation() {
+        return "";
+    }
+
+    public boolean isSmallInsert() {
+        return false;
+    }
+
+    public boolean isVendorFailedRead() {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public Color getDefaultColor() {      
+        return AlignmentRenderer.grey1;
+    }
+
     public String getChromosome() {
         return chromosome;
     }
 
+    public String getChr() {
+        return chromosome;
+    }
+
     public int getAlignmentStart() {
         return getStart();
     }
@@ -190,6 +217,14 @@ public class DotAlignedAlignment implements Alignment {
         return null;
     }
 
+    public Object getAttribute(String key) {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public Strand getFragmentStrand(int readPair) {
+        return Strand.NONE;
+    }
+
     public char[] getGapTypes() {
         return null;
     }
diff --git a/src/org/broad/igv/sam/EWigTrack.java b/src/org/broad/igv/sam/EWigTrack.java
index 506eb86..a2b9573 100644
--- a/src/org/broad/igv/sam/EWigTrack.java
+++ b/src/org/broad/igv/sam/EWigTrack.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -24,10 +24,10 @@ package org.broad.igv.sam;
 
 import com.jidesoft.swing.JidePopupMenu;
 import org.broad.igv.feature.LocusScore;
-import org.broad.igv.tdf.TDFDataSource;
-import org.broad.igv.tdf.TDFReader;
 import org.broad.igv.renderer.DataRange;
 import org.broad.igv.renderer.Renderer;
+import org.broad.igv.tdf.TDFDataSource;
+import org.broad.igv.tdf.TDFReader;
 import org.broad.igv.track.*;
 import org.broad.igv.util.ResourceLocator;
 
@@ -53,12 +53,13 @@ public class EWigTrack extends AbstractTrack {
     TDFDataSource scoreSource;
 
     public EWigTrack(ResourceLocator locator) {
-        super(locator, locator.getDisplayName());
-        TDFReader reader = TDFReader.getReader(locator);
+        super(locator);
+
+        TDFReader reader = TDFReader.getReader(locator.getPath());
         scoreSource = new TDFDataSource(reader, 4, "Pi");
         scoreSource.setAggregateLikeBins(false);
 
-        setDataRange(new DataRange(0, 0, (float) reader.getDataMax()));
+        setDataRange(new DataRange(0, 0, 10));
         baseSources = new HashMap();
         for (int i = 0; i < 4; i++) {
             TDFDataSource src = new TDFDataSource(reader, i, Character.toString(nucleotides[i]));
@@ -96,13 +97,13 @@ public class EWigTrack extends AbstractTrack {
     private void paint(RenderContext context, Rectangle rect) {
 
         List<LocusScore> scores = scoreSource.getSummaryScoresForRange(context.getChr(),
-                (int) context.getStartLocation(),
+                (int) context.getOrigin(),
                 (int) context.getEndLocation(),
                 context.getZoom());
         Map<Character, List<LocusScore>> nScores = new HashMap();
         for (Character c : nucleotides) {
             nScores.put(c, baseSources.get(c).getSummaryScoresForRange(context.getChr(),
-                    (int) context.getStartLocation(),
+                    (int) context.getOrigin(),
                     (int) context.getEndLocation(),
                     context.getZoom()));
         }
@@ -141,7 +142,7 @@ public class EWigTrack extends AbstractTrack {
 
             float dataMax = getDataRange().getMaximum();
 
-            double height = (int) (totalCount * rect.getHeight() / dataMax);
+            double height = (totalCount * rect.getHeight() / dataMax);
             height = Math.min(height, rect.height - 1);
 
             for (char c : nucleotides) {
@@ -150,7 +151,7 @@ public class EWigTrack extends AbstractTrack {
                     float count = ns.getScore() * totalCount;
 
                     //pY = drawBar(context, idx, rect, dataMax, pY, pX, dX, c, count);
-                    Graphics2D tGraphics = context.getGraphic2DForColor(AlignmentRenderer.nucleotideColors.get(c));
+                    Graphics2D tGraphics = context.getGraphic2DForColor(AlignmentRenderer.getNucleotideColors().get(c));
 
                     int h = (int) Math.round(count * height / totalCount);
                     h = Math.min(pY - rect.y, h);
@@ -212,7 +213,7 @@ public class EWigTrack extends AbstractTrack {
 
         JPopupMenu popupMenu = new JidePopupMenu();
 
-        JLabel popupTitle = new JLabel("  " + getDisplayName(), JLabel.CENTER);
+        JLabel popupTitle = new JLabel("  " + getName(), JLabel.CENTER);
 
         Font newFont = popupMenu.getFont().deriveFont(Font.BOLD, 12);
         popupTitle.setFont(newFont);
@@ -236,11 +237,11 @@ public class EWigTrack extends AbstractTrack {
 
         //popupMenu.add(trackSettingsHeading);
 
-        TrackMenuUtils.addTrackRenameItem(popupMenu);
-        TrackMenuUtils.addChangeTrackHeightItem(popupMenu);
         ArrayList<Track> tmp = new ArrayList();
         tmp.add(this);
-        TrackMenuUtils.addDataRangeItem(popupMenu, tmp, true);
+        popupMenu.add(TrackMenuUtils.getTrackRenameItem(tmp));
+        popupMenu.add(TrackMenuUtils.getChangeTrackHeightItem(tmp));
+        popupMenu.add(TrackMenuUtils.getDataRangeItem(tmp));
 
 
         return popupMenu;
diff --git a/src/org/broad/igv/sam/EmptyAlignmentIterator.java b/src/org/broad/igv/sam/EmptyAlignmentIterator.java
index 157d080..d9e4d90 100644
--- a/src/org/broad/igv/sam/EmptyAlignmentIterator.java
+++ b/src/org/broad/igv/sam/EmptyAlignmentIterator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/sam/FeatureRenderer.java b/src/org/broad/igv/sam/FeatureRenderer.java
index 3e12fd6..b979b44 100644
--- a/src/org/broad/igv/sam/FeatureRenderer.java
+++ b/src/org/broad/igv/sam/FeatureRenderer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/sam/FeatureWrappedAlignment.java b/src/org/broad/igv/sam/FeatureWrappedAlignment.java
new file mode 100644
index 0000000..d5b0f11
--- /dev/null
+++ b/src/org/broad/igv/sam/FeatureWrappedAlignment.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.sam;
+
+import org.broad.igv.feature.BasicFeature;
+import org.broad.igv.feature.Exon;
+import org.broad.igv.feature.LocusScore;
+import org.broad.igv.feature.Strand;
+import org.broad.igv.track.WindowFunction;
+
+import java.awt.*;
+
+/**
+ * Some alignment formats are parsed as Features.
+ * <p/>
+ * This is all getting rather ciruclar,  some refactoring is in order.
+ *
+ * @author jrobinso
+ * @date Aug 5, 2010
+ */
+public class FeatureWrappedAlignment implements Alignment {
+    String readName;
+    String chr;
+    int start;
+    int end;
+    AlignmentBlock[] blocks;
+    Strand strand;
+
+    BasicFeature f;
+
+    public FeatureWrappedAlignment(BasicFeature f) {
+
+        this.readName = f.getName();
+        this.chr = f.getChr();
+        this.start = f.getStart();
+        this.end = f.getEnd();
+        strand = f.getStrand();
+
+        if (f.getExonCount() > 0) {
+            blocks = new AlignmentBlock[f.getExonCount()];
+            int i=0;
+            for(Exon exon : f.getExons()) {
+                int length = exon.getLength();
+                byte [] seq = new byte[length];
+                blocks[i] = new AlignmentBlock(exon.getStart(), seq, seq);
+                i++;
+            }
+        }
+    }
+
+    public String getReadName() {
+        return readName;
+    }
+
+    public String getReadSequence() {
+        return null;
+    }
+
+    public String getChromosome() {
+        return chr;
+    }
+
+    public String getChr() {
+        return chr;
+    }
+
+    public int getAlignmentStart() {
+        return start;
+    }
+
+    public boolean contains(double location) {
+        return location >= start && location <= getEnd();
+    }
+
+    public AlignmentBlock[] getAlignmentBlocks() {
+        return blocks;
+    }
+
+    public AlignmentBlock[] getInsertions() {
+        return null;
+    }
+
+    public String getCigarString() {
+        return "*";
+    }
+
+    public int getInferredInsertSize() {
+        return 0;
+    }
+
+    public int getMappingQuality() {
+        return 255;
+    }
+
+    public ReadMate getMate() {
+        return null;
+    }
+
+    public boolean isProperPair() {
+        return true;
+    }
+
+    public boolean isMapped() {
+        return true;
+    }
+
+    public boolean isPaired() {
+        return false;
+    }
+
+    public boolean isNegativeStrand() {
+        return strand == Strand.NEGATIVE;
+    }
+
+    public boolean isDuplicate() {
+        return false;
+    }
+
+    public float getScore() {
+        return 1.0f;
+    }
+
+    public void setConfidence(float confidence) {
+        // ignore
+    }
+
+    public float getConfidence() {
+        return 1.0f;
+    }
+
+    public LocusScore copy() {
+        return this;
+    }
+
+    public String getValueString(double position, WindowFunction windowFunction) {
+        return readName + "<br>Read length = " + (getEnd() - getStart());
+    }
+
+    /**
+     * @return the start
+     */
+    public int getStart() {
+        return start;
+    }
+
+    /**
+     * @param start the start to set
+     */
+    public void setStart(int start) {
+        this.start = start;
+    }
+
+    /**
+     * @return the end
+     */
+    public int getEnd() {
+        return end;
+    }
+
+    public int getAlignmentEnd() {
+        return end;
+    }
+
+    /**
+     * @param end the end to set
+     */
+    public void setEnd(int end) {
+        this.end = end;
+    }
+
+    public byte getBase(double position) {
+        return 0;
+    }
+
+    public byte getPhred(double position) {
+        return 0;
+    }
+
+    public String getSample() {
+        return null;
+    }
+
+    public String getReadGroup() {
+        return null;
+    }
+
+    public Object getAttribute(String key) {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public Strand getFragmentStrand(int readPair) {
+        return Strand.NONE;
+    }
+
+    public void setMateSequence(String sequence) {
+        //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public String getPairOrientation() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean isSmallInsert() {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean isVendorFailedRead() {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public Color getDefaultColor() {
+        return AlignmentRenderer.grey1;
+    }
+
+    public char[] getGapTypes() {
+        return null;
+    }
+}
diff --git a/src/org/broad/igv/sam/GeraldAlignment.java b/src/org/broad/igv/sam/GeraldAlignment.java
index 7c24f36..2194bc7 100644
--- a/src/org/broad/igv/sam/GeraldAlignment.java
+++ b/src/org/broad/igv/sam/GeraldAlignment.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,6 +23,7 @@
 package org.broad.igv.sam;
 
 import org.broad.igv.feature.LocusScore;
+import org.broad.igv.feature.Strand;
 import org.broad.igv.track.WindowFunction;
 
 /**
@@ -94,7 +95,7 @@ public class GeraldAlignment extends AbstractAlignment implements Alignment {
     }
 
     public boolean isMapped() {
-        return getChromosome() != null;
+        return getChr() != null;
     }
 
     public boolean isPaired() {
@@ -139,5 +140,8 @@ public class GeraldAlignment extends AbstractAlignment implements Alignment {
         return null;
     }
 
+    public Strand getFragmentStrand(int read) {
+        return Strand.NONE;
+    }
 
 }
diff --git a/src/org/broad/igv/sam/ReadMate.java b/src/org/broad/igv/sam/ReadMate.java
index cced77f..a4103be 100644
--- a/src/org/broad/igv/sam/ReadMate.java
+++ b/src/org/broad/igv/sam/ReadMate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/sam/SamAlignment.java b/src/org/broad/igv/sam/SamAlignment.java
index 27b0265..48850b6 100644
--- a/src/org/broad/igv/sam/SamAlignment.java
+++ b/src/org/broad/igv/sam/SamAlignment.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,10 +23,15 @@ import net.sf.samtools.SAMFileHeader;
 import net.sf.samtools.SAMReadGroupRecord;
 import net.sf.samtools.SAMRecord;
 import org.apache.log4j.Logger;
+import org.broad.igv.PreferenceManager;
 import org.broad.igv.feature.Genome;
 import org.broad.igv.feature.LocusScore;
-import org.broad.igv.ui.IGVModel;
+import org.broad.igv.feature.Strand;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.track.WindowFunction;
+import org.broad.igv.util.ColorUtilities;
 
+import java.awt.*;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -50,6 +55,8 @@ public class SamAlignment extends AbstractAlignment implements Alignment {
     public static final char HARD_CLIP = 'H';
     public static final char PADDING = 'P';
     public static final char ZERO_GAP = 'O';
+    private int start;  // <= Might differ from alignment start if soft clipping is considered
+    private int end;    // ditto
     private int alignmentStart;
     private int alignmentEnd;
     boolean negativeStrand;
@@ -63,6 +70,13 @@ public class SamAlignment extends AbstractAlignment implements Alignment {
     String readSequence;
     private boolean softClippedStart = false;
     private boolean softClippedEnd = false;
+    private Strand firstReadStrand = Strand.NONE;
+    private Strand secondReadStrand = Strand.NONE;
+    boolean firstRead = false;
+    boolean secondRead = false;
+    private String mateSequence = null;
+    private String pairOrientation = "";
+    private Color defaultColor = AlignmentRenderer.grey1;
 
     /**
      * Constructs ...
@@ -76,13 +90,15 @@ public class SamAlignment extends AbstractAlignment implements Alignment {
 
         String refName = record.getReferenceName();
 
-        Genome genome = IGVModel.getInstance().getViewContext().getGenome();
-        this.chr = genome.getChromosomeAlias(refName);
+        Genome genome = ViewContext.getInstance().getGenome();
+        this.chr = genome == null ? refName : genome.getChromosomeAlias(refName);
 
 
         // SAMRecord is 1 based inclusive.  IGV is 0 based exclusive.
         this.alignmentStart = record.getAlignmentStart() - 1;
-        this.alignmentEnd = record.getAlignmentEnd();
+        this.start = this.alignmentStart;   // might be modified later for soft clipping
+        this.alignmentEnd = Math.max(alignmentStart, record.getAlignmentEnd());
+        this.end = alignmentEnd;   // might be modified later for soft clipping
         this.negativeStrand = record.getReadNegativeStrandFlag();
         this.cigarString = record.getCigarString();
         this.setMappingQuality(record.getMappingQuality());
@@ -94,16 +110,88 @@ public class SamAlignment extends AbstractAlignment implements Alignment {
         this.setInferredInsertSize(record.getInferredInsertSize());
         this.readSequence = record.getReadString();
 
+        setMatePair(record, genome);
+        setPairOrientation(record);
+        setFirstReadStrand(record);
+        createAlignmentBlocks(record.getCigarString(), record.getReadBases(), record.getBaseQualities());
+
+        Object colorTag = record.getAttribute("YC");
+        if (colorTag != null) {
+            try {
+                defaultColor = ColorUtilities.convertRGBStringToColor(colorTag.toString());
+            } catch (Exception e) {
+                log.error("Error interpreting color tag: " + colorTag, e);
+                defaultColor = AlignmentRenderer.grey1;
+            }
+        }
+    }
+
+    private void setFirstReadStrand(SAMRecord record) {
+        if (!record.getReadPairedFlag()) {
+            firstReadStrand = (record.getReadNegativeStrandFlag() ? Strand.NEGATIVE : Strand.POSITIVE);
+        } else if (record.getProperPairFlag()) {
+            if (record.getFirstOfPairFlag()) {
+                firstReadStrand = (record.getReadNegativeStrandFlag() ? Strand.NEGATIVE : Strand.POSITIVE);
+                if (!record.getMateUnmappedFlag()) {
+                    secondReadStrand = (record.getMateNegativeStrandFlag() ? Strand.NEGATIVE : Strand.POSITIVE);
+                }
+            } else {
+                if (!record.getMateUnmappedFlag()) {
+                    firstReadStrand = (record.getMateNegativeStrandFlag() ? Strand.NEGATIVE : Strand.POSITIVE);
+                }
+                secondReadStrand = (record.getReadNegativeStrandFlag() ? Strand.NEGATIVE : Strand.POSITIVE);
+            }
+        }
+    }
+
+    private void setMatePair(SAMRecord record, Genome genome) {
         if (record.getReadPairedFlag()) {
-            String mateChr = genome.getChromosomeAlias(record.getMateReferenceName());
+            String mateReferenceName = record.getMateReferenceName();
+            String mateChr = genome == null ? mateReferenceName : genome.getChromosomeAlias(mateReferenceName);
             this.properPairFlag = record.getProperPairFlag();
             this.setMate(new ReadMate(mateChr,
                     record.getMateAlignmentStart(),
                     record.getMateNegativeStrandFlag(),
                     record.getMateUnmappedFlag()));
+            this.firstRead = record.getFirstOfPairFlag();
+            this.secondRead = record.getSecondOfPairFlag();
         }
 
-        createAlignmentBlocks(record.getCigarString(), record.getReadBases(), record.getBaseQualities());
+    }
+
+    private char[] tmp = new char[4];
+
+    private void setPairOrientation(SAMRecord record) {
+        if (record.getReadPairedFlag() &&
+                !readUnmappedFlag &&
+                !record.getMateUnmappedFlag() &&
+                record.getReferenceName().equals(record.getMateReferenceName())) {
+
+            char s1 = record.getReadNegativeStrandFlag() ? 'R' : 'F';
+            char s2 = record.getMateNegativeStrandFlag() ? 'R' : 'F';
+            char o1 = ' ';
+            char o2 = ' ';
+            if (record.getFirstOfPairFlag()) {
+                o1 = '1';
+                o2 = '2';
+            } else if (record.getSecondOfPairFlag()) {
+                o1 = '2';
+                o2 = '1';
+            }
+            if (record.getInferredInsertSize() > 0) {
+                tmp[0] = s1;
+                tmp[1] = o1;
+                tmp[2] = s2;
+                tmp[3] = o2;
+
+            } else {
+                tmp[2] = s1;
+                tmp[3] = o1;
+                tmp[0] = s2;
+                tmp[1] = o2;
+            }
+            pairOrientation = new String(tmp);
+        }
     }
 
     /**
@@ -113,6 +201,7 @@ public class SamAlignment extends AbstractAlignment implements Alignment {
         this.chr = alignment.chr;
         this.alignmentStart = alignment.alignmentStart;
         this.alignmentEnd = alignment.alignmentEnd;
+        this.end = alignment.end;
         this.negativeStrand = alignment.negativeStrand;
         this.mate = alignment.mate;
         this.alignmentBlocks = alignment.alignmentBlocks;
@@ -143,8 +232,12 @@ public class SamAlignment extends AbstractAlignment implements Alignment {
      * @param readBases
      * @param readBaseQualities
      */
+
     void createAlignmentBlocks(String cigarString, byte[] readBases, byte[] readBaseQualities) {
 
+        boolean showSoftClipped = PreferenceManager.getInstance().getSAMPreferences().isShowSoftClipped();
+
+
         int nInsertions = 0;
         int nBlocks = 0;
 
@@ -160,7 +253,7 @@ public class SamAlignment extends AbstractAlignment implements Alignment {
         boolean firstOperator = true;
         int softClippedBaseCount = 0;
         int nGaps = 0;
-        char lastOperator = 0;
+        char prevOp = 0;
         for (int i = 0; i < cigarString.length(); i++) {
             char next = cigarString.charAt(i);
             if (Character.isDigit(next)) {
@@ -168,23 +261,29 @@ public class SamAlignment extends AbstractAlignment implements Alignment {
             } else {
                 int nBases = Integer.parseInt(buffer.toString());
                 char op = next;
-                if (op == MATCH || op == PERFECT_MATCH || op == MISMATCH) {
-                    if (lastOperator == MATCH || lastOperator == PERFECT_MATCH || lastOperator == MISMATCH) {
+                if (op == HARD_CLIP) {
+                    buffer = new StringBuffer(4);
+                    continue;  // Just skip hardclips
+                } else if (operatorIsMatch(showSoftClipped, op)) {
+                    if (operatorIsMatch(showSoftClipped, prevOp)) {
                         nGaps++;   // Consecutive Ms
                     }
                     nBlocks++;
-                } else if (op == SOFT_CLIP && firstOperator) {
-                    softClippedBaseCount += nBases;
+
                 } else if (op == DELETION || op == SKIPPED_REGION) {
                     nGaps++;
                 } else if (op == INSERTION) {
                     nInsertions++;
                     nGaps++; // "virtual" gap, account for artificial block split @ insertion
                 }
+                if (firstOperator && op == SOFT_CLIP) {
+                    softClippedBaseCount += nBases;
+                }
+
                 operators.add(new CigarOperator(nBases, op));
                 buffer = new StringBuffer(4);
 
-                lastOperator = op;
+                prevOp = op;
                 firstOperator = false;
             }
 
@@ -192,27 +291,32 @@ public class SamAlignment extends AbstractAlignment implements Alignment {
 
         alignmentBlocks = new AlignmentBlock[nBlocks];
         insertions = new AlignmentBlock[nInsertions];
-        if (nGaps > 0) {
+        if (nGaps > 0)
+
+        {
             gapTypes = new char[nGaps];
         }
 
-        // fill new arrays
-        int fromIdx = softClippedBaseCount;
-        int blockStart = getStart();
+        // Adjust start and fill new arrays
+        if (showSoftClipped) {
+            start -= softClippedBaseCount;
+        }
+        int fromIdx = showSoftClipped ? 0 : softClippedBaseCount;
+        int blockStart = start;
+
         int blockIdx = 0;
         int insertionIdx = 0;
         int gapIdx = 0;
-        lastOperator = 0;
+        prevOp = 0;
         for (CigarOperator op : operators) {
-            if (op.operator == MATCH || op.operator == PERFECT_MATCH || op.operator == MISMATCH) {
-                byte[] blockBases = new byte[op.nBases];
+            if (operatorIsMatch(showSoftClipped, op.operator)) {
 
+                byte[] blockBases = new byte[op.nBases];
                 byte[] blockQualities = new byte[op.nBases];
-                // Default value
+                //Default value
                 Arrays.fill(blockQualities, (byte) 126);
 
                 // TODO -- represent missing sequence ("*") explicitly rather.
-                // This is inefficient with respect to space
                 if (readBases == null || readBases.length == 0) {
                     Arrays.fill(blockBases, (byte) '=');
                 } else {
@@ -225,12 +329,16 @@ public class SamAlignment extends AbstractAlignment implements Alignment {
                     System.arraycopy(readBaseQualities, fromIdx, blockQualities, 0, op.nBases);
                 }
 
-                alignmentBlocks[blockIdx++] = new AlignmentBlock(blockStart, blockBases, blockQualities);
+                AlignmentBlock block = new AlignmentBlock(blockStart, blockBases, blockQualities);
+                if (op.operator == SOFT_CLIP) {
+                    block.setSoftClipped(true);
+                }
+                alignmentBlocks[blockIdx++] = block;
 
                 fromIdx += op.nBases;
                 blockStart += op.nBases;
 
-                if (lastOperator == MATCH || lastOperator == PERFECT_MATCH || lastOperator == MISMATCH) {
+                if (operatorIsMatch(showSoftClipped, prevOp)) {
                     gapTypes[gapIdx++] = ZERO_GAP;
                 }
 
@@ -246,8 +354,7 @@ public class SamAlignment extends AbstractAlignment implements Alignment {
                 byte[] blockBases = new byte[op.nBases];
                 byte[] blockQualities = new byte[op.nBases];
 
-                // TODO -- represent missing sequence ("*") explicitly  
-                 if (readBases == null || readBases.length == 0) {
+                if (readBases == null || readBases.length == 0) {
                     Arrays.fill(blockBases, (byte) '=');
                 } else {
                     System.arraycopy(readBases, fromIdx, blockBases, 0, op.nBases);
@@ -259,18 +366,32 @@ public class SamAlignment extends AbstractAlignment implements Alignment {
                     System.arraycopy(readBaseQualities, fromIdx, blockQualities, 0, op.nBases);
                 }
 
-                insertions[insertionIdx++] = new AlignmentBlock(blockStart, blockBases,
-                        blockQualities);
+                insertions[insertionIdx++] = new AlignmentBlock(blockStart, blockBases, blockQualities);
 
                 fromIdx += op.nBases;
 
             }
-            lastOperator = op.operator;
+            prevOp = op.operator;
         }
 
+        // Check for soft clipping at end
+        if (showSoftClipped && operators.size() > 0) {
+            CigarOperator last = operators.get(operators.size() - 1);
+            if (last.operator == SOFT_CLIP) {
+                end += last.nBases;
+            }
+        }
+
+
+    }
+
+    private boolean operatorIsMatch(boolean showSoftClipped, char operator) {
+        return operator == MATCH || operator == PERFECT_MATCH || operator == MISMATCH
+                || (showSoftClipped && operator == SOFT_CLIP);
     }
 
     // Default constructor to support unit tests
+
     SamAlignment() {
     }
 
@@ -294,6 +415,7 @@ public class SamAlignment extends AbstractAlignment implements Alignment {
         return properPairFlag;
     }
 
+
     /**
      * TODO
      *
@@ -334,19 +456,21 @@ public class SamAlignment extends AbstractAlignment implements Alignment {
     }
 
     public int getStart() {
-        return alignmentStart;
+        return start;
     }
 
     public void setStart(int start) {
+        this.start = start;
         this.alignmentStart = start;
     }
 
     public int getEnd() {
-        return alignmentEnd;
+        return end;
     }
 
     public void setEnd(int end) {
-        this.alignmentEnd = alignmentEnd;
+        this.end = end;
+        this.alignmentEnd = end;
     }
 
     /**
@@ -398,6 +522,82 @@ public class SamAlignment extends AbstractAlignment implements Alignment {
         return gapTypes;
     }
 
+    public Strand getFragmentStrand(int strand) {
+        return strand == 1 ? firstReadStrand : secondReadStrand;
+    }
+
+    public Object getAttribute(String key) {
+        return record.getAttribute(key);
+    }
+
+    /**
+     * Return info string for popup text and to copy to clipboard
+     *
+     * @param position
+     * @param windowFunction -- not relevant, ignored
+     * @return
+     */
+    public String getValueString(double position, WindowFunction windowFunction) {
+        StringBuffer buf = new StringBuffer(super.getValueString(position, windowFunction));
+
+        if (isPaired()) {
+            if (record.getFirstOfPairFlag()) {
+                buf.append("<br>First in pair");
+            }
+            if (record.getSecondOfPairFlag()) {
+                buf.append("<br>Second in pair");
+            }
+            if (record.getNotPrimaryAlignmentFlag()) {
+                buf.append("<br>Alignment NOT primary");
+            }
+            if (record.getReadFailsVendorQualityCheckFlag()) {
+                buf.append("<br>FAILED Vendor Quality Check");
+            }
+            buf.append("<br>-------------------");
+        }
+
+        List<SAMRecord.SAMTagAndValue> attributes = record.getAttributes();
+        if (attributes != null && !attributes.isEmpty()) {
+
+            for (SAMRecord.SAMTagAndValue tag : attributes) {
+                buf.append("<br>" + tag.tag + " = " + tag.value);
+            }
+            buf.append("<br>-------------------");
+        }
+
+        if (mateSequence != null) {
+            buf.append("<br>Unmapped mate sequence: " + mateSequence);
+            buf.append("<br>-------------------");
+        }
+        return buf.toString();
+
+    }
+
+    public boolean isFirstInPair() {
+        return record.getFirstOfPairFlag();
+    }
+
+    @Override
+    public String getMateSequence() {
+        return mateSequence;
+    }
+
+    public void setMateSequence(String mateSequence) {
+        this.mateSequence = mateSequence;
+    }
+
+    public String getPairOrientation() {
+        return pairOrientation;
+    }
+
+    public boolean isVendorFailedRead() {
+        return record.getReadFailsVendorQualityCheckFlag();
+    }
+
+    public Color getDefaultColor() {
+        return defaultColor;
+    }
+
     static class CigarOperator {
 
         int nBases;
diff --git a/src/org/broad/igv/sam/reader/AlignmentIndexer.java b/src/org/broad/igv/sam/reader/AlignmentIndexer.java
index c1f78ae..491d1e6 100644
--- a/src/org/broad/igv/sam/reader/AlignmentIndexer.java
+++ b/src/org/broad/igv/sam/reader/AlignmentIndexer.java
@@ -17,8 +17,8 @@
  */
 package org.broad.igv.sam.reader;
 
+import org.broad.igv.ui.util.UIUtilities;
 import org.broad.igv.util.ParsingUtils;
-import org.broad.igv.ui.GuiUtilities;
 import org.broad.igv.util.AsciiLineReader;
 
 import javax.swing.*;
@@ -100,7 +100,7 @@ public abstract class AlignmentIndexer {
         FeatureIndex featureIndex = new FeatureIndex(tileWidth);
         int recordCount = 0;
         long filePosition = 0;
-        int lastTileRecorded = 0;
+        int currentTile = 0;
         int longestFeature = 0;
 
         long startTime = System.currentTimeMillis();
@@ -120,23 +120,29 @@ public abstract class AlignmentIndexer {
             nextLine = nextLine.trim();
             int nFields = ParsingUtils.split(nextLine, fields, '\t');
             if (!nextLine.startsWith("@") && nFields > 3 && isMapped(fields)) {
-                String chr = getChromosome(fields);
 
-                if (lastChr == null || !chr.equals(lastChr)) {
-                    // New chromosome.  Start a new section
-                    if (lastChr != null) {
-                        featureIndex.add(lastChr, filePosition, recordCount, longestFeature);
-                        filePosition = lastFilePosition;
-                        lastTileRecorded = 0;
-                        recordCount = 0;
-                        lastAlignmentStart = 0;
-                        longestFeature = 0;
+                String chr = getChromosome(fields);
+                int alignmentStart = getAlignmentStart(fields);
+                int tileNumber = alignmentStart / tileWidth;
+
+                if (lastChr == null) {
+                    // First record
+                    currentTile = tileNumber;
+                    for (int i = 0; i < currentTile; i++) {
+                        featureIndex.add(chr, lastFilePosition, 0, longestFeature);
                     }
+                    lastChr = chr;
 
+                } else if (!chr.equals(lastChr)) {   // New chromosome
+                    featureIndex.add(lastChr, filePosition, recordCount, longestFeature);
+                    filePosition = lastFilePosition;
+                    
+                    currentTile = 0;
+                    recordCount = 0;
+                    lastAlignmentStart = 0;
+                    longestFeature = 0;
                     lastChr = chr;
                 } else {
-                    // Get alignmetn start and verify file is sorted.
-                    int alignmentStart = getAlignmentStart(fields);
 
                     longestFeature = Math.max(longestFeature, getAlignmentLength(fields));
 
@@ -152,19 +158,18 @@ public abstract class AlignmentIndexer {
 
                     lastAlignmentStart = alignmentStart;
 
-                    int tileNumber = alignmentStart / tileWidth;
-                    if (tileNumber > lastTileRecorded) {
+                    if (tileNumber > currentTile) {
 
                         // We have crossed a tile boundary.  Record index and counts for previous tile
                         featureIndex.add(lastChr, filePosition, recordCount, longestFeature);
 
                         // If tiles were skipped record zero counts for these.
-                        for (int cnt = 0; cnt < (tileNumber - lastTileRecorded - 1); cnt++) {
+                        for (int cnt = 0; cnt < (tileNumber - currentTile - 1); cnt++) {
                             featureIndex.add(lastChr, filePosition, 0, longestFeature);
                         }
 
                         filePosition = lastFilePosition;
-                        lastTileRecorded = tileNumber;
+                        currentTile = tileNumber;
                         recordCount = 0;
                     }
                     recordCount++;
@@ -207,13 +212,12 @@ public abstract class AlignmentIndexer {
     abstract boolean isMapped(String[] fields);
 
 
-
     private void updateProgress(int progressCounter, long startTime) {
         final long timeToComplete = ((100 - progressCounter) *
                 (System.currentTimeMillis() - startTime)) / progressCounter;
         final int p = progressCounter;
         if (progressBar != null) {
-            GuiUtilities.invokeOnEventThread(new Runnable() {
+            UIUtilities.invokeOnEventThread(new Runnable() {
 
                 public void run() {
                     progressBar.setValue(p);
diff --git a/src/org/broad/igv/sam/reader/AlignmentParser.java b/src/org/broad/igv/sam/reader/AlignmentParser.java
index 0853716..14a1b8e 100644
--- a/src/org/broad/igv/sam/reader/AlignmentParser.java
+++ b/src/org/broad/igv/sam/reader/AlignmentParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/sam/reader/AlignmentQueryReader.java b/src/org/broad/igv/sam/reader/AlignmentQueryReader.java
index ea044e4..5bbeb96 100644
--- a/src/org/broad/igv/sam/reader/AlignmentQueryReader.java
+++ b/src/org/broad/igv/sam/reader/AlignmentQueryReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -28,6 +28,7 @@ import net.sf.samtools.util.CloseableIterator;
 import org.broad.igv.sam.Alignment;
 
 import java.io.IOException;
+import java.util.Set;
 
 /**
  * @author jrobinso
@@ -36,6 +37,8 @@ public interface AlignmentQueryReader {
 
     void close() throws IOException;
 
+    Set<String> getSequenceNames();
+
     SAMFileHeader getHeader();
 
     CloseableIterator<Alignment> iterator();
diff --git a/src/org/broad/igv/sam/reader/BAMHttpQueryReader.java b/src/org/broad/igv/sam/reader/BAMHttpQueryReader.java
index e1ba07a..156f10a 100644
--- a/src/org/broad/igv/sam/reader/BAMHttpQueryReader.java
+++ b/src/org/broad/igv/sam/reader/BAMHttpQueryReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -21,18 +21,20 @@ package org.broad.igv.sam.reader;
 import net.sf.samtools.SAMFileHeader;
 import net.sf.samtools.SAMFileReader;
 import net.sf.samtools.SAMRecord;
+import net.sf.samtools.SAMSequenceRecord;
 import net.sf.samtools.util.CloseableIterator;
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
-import org.broad.igv.ui.util.MessageUtils;
+import org.broad.igv.exceptions.DataLoadException;
 import org.broad.igv.sam.Alignment;
-import org.broad.igv.util.HttpUtils;
+import org.broad.igv.ui.UIConstants;
+import org.broad.igv.ui.util.MessageUtils;
+import org.broad.igv.util.IGVHttpUtils;
+import org.broad.igv.util.SeekableStreamFactory;
 
 import java.io.*;
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.net.URLConnection;
-import java.util.Properties;
+import java.util.*;
 
 /**
  * Created by IntelliJ IDEA.
@@ -50,10 +52,13 @@ public class BAMHttpQueryReader implements AlignmentQueryReader {
     URL url;
     SAMFileHeader header;
     File indexFile;
+    SAMFileReader reader;
 
-    public BAMHttpQueryReader(URL url) {
+    public BAMHttpQueryReader(URL url, boolean requireIndex) {
         this.url = url;
-        indexFile = getIndexFile(url);
+        if (requireIndex) {
+            indexFile = getIndexFile(url);
+        }
     }
 
     public void close() throws IOException {
@@ -71,11 +76,28 @@ public class BAMHttpQueryReader implements AlignmentQueryReader {
         return indexFile != null && indexFile.exists();
     }
 
+    public Set<String> getSequenceNames() {
+        SAMFileHeader header = getHeader();
+        if (header == null) {
+            return null;
+        }
+        Set<String> seqNames = new HashSet();
+        List<SAMSequenceRecord> records = header.getSequenceDictionary().getSequences();
+        if (records.size() > 0) {
+            for (SAMSequenceRecord rec : header.getSequenceDictionary().getSequences()) {
+                String chr = rec.getSequenceName();
+                seqNames.add(chr);
+            }
+        }
+        return seqNames;
+    }
+
     public CloseableIterator<Alignment> iterator() {
         try {
-
-            InputStream is = url.openStream();
-            SAMFileReader reader = new SAMFileReader(new BufferedInputStream(is));
+            if (reader == null) {
+                InputStream is = url.openStream();
+                reader = new SAMFileReader(new BufferedInputStream(is));
+            }
             return new WrappedIterator(reader.iterator());
         }
         catch (IOException e) {
@@ -86,7 +108,10 @@ public class BAMHttpQueryReader implements AlignmentQueryReader {
     }
 
     public CloseableIterator<Alignment> query(String sequence, int start, int end, boolean contained) {
-        SAMFileReader reader = new SAMFileReader(url, indexFile, false);
+        if (reader == null) {
+            reader = new SAMFileReader(url, indexFile, false);
+        }
+        log.debug("Query " + sequence + ":" + start + "-" + end);
         CloseableIterator<SAMRecord> iter = reader.query(sequence, start, end, contained);
         return new WrappedIterator(iter);
     }
@@ -97,11 +122,11 @@ public class BAMHttpQueryReader implements AlignmentQueryReader {
         InputStream is = null;
         SAMFileReader reader = null;
         try {
-            URLConnection conn = url.openConnection();
-            conn.setReadTimeout(5000);
+            Map<String, String> props = new HashMap();
             String byteRange = "bytes=0-1000000";
-            conn.setRequestProperty("Range", byteRange);
-            is = conn.getInputStream();
+            props.put("Range", byteRange);
+
+            is = SeekableStreamFactory.getStreamFor(url.toString()); //  IGVHttpUtils.openConnectionStream(url);
             BufferedInputStream bis = new BufferedInputStream(is);
             reader = new SAMFileReader(bis);
             header = reader.getFileHeader();
@@ -127,53 +152,24 @@ public class BAMHttpQueryReader implements AlignmentQueryReader {
     }
 
 
-    File getIndexFile(URL bamURL) {
-
-        try {
-            String urlString = bamURL.toString() + ".bai";
-            URL indexURL = new URL(urlString);
-            String eTag = HttpUtils.getETag(indexURL);
-
-            // Try other naming convention
-            if (eTag == null) {
-                urlString = bamURL.toString().replace(".bam", ".bai");
-                indexURL = new URL(urlString);
-                eTag = HttpUtils.getETag(indexURL);
-            }
+    // TODO -- revisit caching scehme,  do something for ftp loads
 
+    File getIndexFile(URL bamURL) {
 
-            // Create a filename unique for this url;
-            String idxFilename = getIndexFilename(bamURL.toString());
-            File indexFile = new File(this.getCacheDirectory(), idxFilename);
+        String bamURLString = bamURL.toString();
 
-            Properties eTagCache = getTagCache();
-            if (indexFile.exists()) {
-                String cachedETag = eTagCache.getProperty(urlString);
-                if (eTag == null || cachedETag == null || !eTag.equals(cachedETag)) {
-                    indexFile.delete();
-                    eTagCache.remove(urlString);
-                    writeCachedProperties(eTagCache, "etags");
-                }
-            }
+        String idxFilename = getCachedIndexFilename(bamURLString);
 
-            indexFile = new File(this.getCacheDirectory(), idxFilename);
-            if (!indexFile.exists()) {
-                loadIndexFile(indexURL, indexFile);
-                if (eTag != null) {
-                    eTagCache.put(urlString, eTag);
-                    writeCachedProperties(eTagCache, "etags");
-                }
-            }
-            return indexFile;
-        } catch (MalformedURLException e) {
-            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
-            return null;
+        indexFile = new File(this.getCacheDirectory(), idxFilename);
+        if (!indexFile.exists()) {
+            loadIndexFile(bamURL.toString(), indexFile);
+            indexFile.deleteOnExit();
         }
-    }
+        return indexFile;
 
-    private String getIndexFilename(String bamURL) {
+    }
 
-        int maxTries = 10;
+    private String getCachedIndexFilename(String bamURL) {
 
         Properties lookup = getIndexCache();
         String indexName = lookup.getProperty(bamURL);
@@ -188,23 +184,38 @@ public class BAMHttpQueryReader implements AlignmentQueryReader {
         return indexName;
     }
 
-    private void loadIndexFile(URL indexURL, File indexFile) {
+    private void loadIndexFile(String bamURL, File indexFile) {
         InputStream is = null;
         OutputStream os = null;
+
         try {
-            is = indexURL.openStream();
+            URL indexURL = new URL(bamURL + ".bai");
             os = new FileOutputStream(indexFile);
+            try {
+                is = IGVHttpUtils.openConnectionStream(indexURL);
 
-            byte[] buf = new byte[4 * 1024]; // 4K buffer
+            }
+            catch (FileNotFoundException e) {
+                // Try other index convention
+                String baseName = bamURL.substring(0, bamURL.length() - 4);
+                indexURL = new URL(baseName + ".bai");
+
+                try {
+                    is = IGVHttpUtils.openConnectionStream(indexURL);
+                }
+                catch (FileNotFoundException e1) {
+                    MessageUtils.showMessage("Index file not found for file: " + bamURL);
+                    throw new DataLoadException("Index file not found for file: " + bamURL, bamURL);
+                }
+            }
+            byte[] buf = new byte[512000];
             int bytesRead;
             while ((bytesRead = is.read(buf)) != -1) {
                 os.write(buf, 0, bytesRead);
             }
+
         }
-        catch (FileNotFoundException e) {
-            MessageUtils.showMessage("Index file not foud: " + indexFile.getAbsolutePath());
-        }
-        catch(Exception e) {
+        catch (IOException e) {
             MessageUtils.showMessage("Error loading index file: " + indexFile.getAbsolutePath());
             log.error("Error downloading index file ", e);
 
@@ -234,7 +245,7 @@ public class BAMHttpQueryReader implements AlignmentQueryReader {
 
     static synchronized File getCacheDirectory() {
         if (cacheDirectory == null) {
-            File defaultDir = new File(IGVConstants.DEFAULT_IGV_DIRECTORY);
+            File defaultDir = UIConstants.getIgvDirectory();
             if (defaultDir.exists()) {
                 cacheDirectory = new File(defaultDir, "bam");
                 if (!cacheDirectory.exists()) {
diff --git a/src/org/broad/igv/sam/reader/BAMQueryReader.java b/src/org/broad/igv/sam/reader/BAMQueryReader.java
index 677658d..86b9d42 100644
--- a/src/org/broad/igv/sam/reader/BAMQueryReader.java
+++ b/src/org/broad/igv/sam/reader/BAMQueryReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -25,12 +25,16 @@ package org.broad.igv.sam.reader;
 import net.sf.samtools.SAMFileHeader;
 import net.sf.samtools.SAMFileReader;
 import net.sf.samtools.SAMFileReader.ValidationStringency;
+import net.sf.samtools.SAMSequenceRecord;
 import net.sf.samtools.util.CloseableIterator;
 import org.broad.igv.sam.Alignment;
 import org.broad.igv.ui.util.MessageUtils;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 
 /**
  * @author jrobinso
@@ -58,6 +62,22 @@ public class BAMQueryReader implements AlignmentQueryReader {
         return header;
     }
 
+    public Set<String> getSequenceNames() {
+        SAMFileHeader header = getHeader();
+        if (header == null) {
+            return null;
+        }
+        Set<String> seqNames = new HashSet();
+        List<SAMSequenceRecord> records = header.getSequenceDictionary().getSequences();
+        if (records.size() > 0) {
+            for (SAMSequenceRecord rec : header.getSequenceDictionary().getSequences()) {
+                String chr = rec.getSequenceName();
+                seqNames.add(chr);
+            }
+        }
+        return seqNames;
+    }
+
     private void loadHeader() {
         header = reader.getFileHeader();
     }
diff --git a/src/org/broad/igv/sam/reader/BAMRemoteQueryReader.java b/src/org/broad/igv/sam/reader/BAMRemoteQueryReader.java
new file mode 100644
index 0000000..dfb363e
--- /dev/null
+++ b/src/org/broad/igv/sam/reader/BAMRemoteQueryReader.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.sam.reader;
+
+import net.sf.samtools.*;
+import net.sf.samtools.util.AsciiLineReader;
+import net.sf.samtools.util.CloseableIterator;
+import net.sf.samtools.util.LineReader;
+import org.apache.log4j.Logger;
+import org.broad.igv.sam.Alignment;
+import org.broad.igv.sam.SamAlignment;
+import org.broad.igv.util.IGVHttpUtils;
+import org.broad.igv.util.ResourceLocator;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * @author jrobinso
+ */
+public class BAMRemoteQueryReader implements AlignmentQueryReader {
+
+    Logger log = Logger.getLogger(BAMRemoteQueryReader.class);
+    String serverURL;
+    String file;
+    SAMFileHeader header;
+
+    public BAMRemoteQueryReader(ResourceLocator locator) {
+        this.serverURL = locator.getServerURL();
+        this.file = locator.getPath();
+        loadHeader();
+    }
+
+    public void close() throws IOException {
+        // Nothing to do
+    }
+
+    public boolean hasIndex() {
+        // There is no server API to to check this, so we'll be optimisitic.
+        return true;
+    }
+
+    public CloseableIterator<Alignment> query(String chr, int start, int end, boolean contained) {
+        try {
+            URL url = new URL(serverURL + "?method=samQuery&samFile=" + file + "&chr=" +
+                    chr + "&start=" + start + "&end=" + end + "&contained=" + contained);
+            InputStream is = IGVHttpUtils.openConnectionStream(url);
+            return new RemoteQueryIterator(new GZIPInputStream(is, 8192));
+
+        } catch (IOException ex) {
+            log.error("Error opening file", ex);
+            throw new RuntimeException(ex);
+        }
+    }
+
+    public SAMFileHeader getHeader() {
+        if (header == null) {
+            loadHeader();
+        }
+        return header;
+    }
+
+    public Set<String> getSequenceNames() {
+        SAMFileHeader header = getHeader();
+        if (header == null) {
+            return null;
+        }
+        Set<String> seqNames = new HashSet();
+        List<SAMSequenceRecord> records = header.getSequenceDictionary().getSequences();
+        if (records.size() > 0) {
+            boolean ensembleChrConventions = true;
+            for (SAMSequenceRecord rec : header.getSequenceDictionary().getSequences()) {
+                String chr = rec.getSequenceName();
+                seqNames.add(chr);
+            }
+
+        }
+        return seqNames;
+    }
+
+    private void loadHeader() {
+        InputStream is = null;
+        try {
+            URL url = new URL(serverURL + "?method=samHeader&samFile=" + file);
+            is = IGVHttpUtils.openConnectionStream(url);
+
+            LineReader reader = new AsciiLineReader(is);
+            SAMTextHeaderCodec code = new SAMTextHeaderCodec();
+            header = code.decode(reader, null);
+
+        } catch (IOException ex) {
+            log.error("Error opening file", ex);
+            throw new RuntimeException(ex);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException ex) {
+                    log.error("Error closing url stream", ex);
+                }
+            }
+        }
+    }
+
+    public CloseableIterator<Alignment> iterator() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    class RemoteQueryIterator implements CloseableIterator<Alignment> {
+
+        InputStream inputStream;
+        SAMRecord nextRecord;
+        BAMRecordCodec codec;
+
+        public RemoteQueryIterator(InputStream is) {
+            this.inputStream = is;
+            codec = new BAMRecordCodec(header);
+            codec.setInputStream(is);
+            advance();
+        }
+
+        private void advance() {
+            nextRecord = codec.decode();
+        }
+
+        public void close() {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                    inputStream = null;
+                } catch (IOException ex) {
+                    log.error("Error closing sam record stream", ex);
+                }
+            }
+        }
+
+        public boolean hasNext() {
+            return nextRecord != null;
+        }
+
+        public SamAlignment next() {
+            SamAlignment ret = new SamAlignment(nextRecord);
+            advance();
+            return ret;
+        }
+
+        public void remove() {
+            // ignored
+        }
+
+        // Just in case caller forgot to close the iterator
+
+        @Override
+        protected void finalize() throws Throwable {
+            super.finalize();
+            if (inputStream != null) {
+                inputStream.close();
+            }
+        }
+    }
+}
diff --git a/src/org/broad/igv/sam/reader/CachingQueryReader.java b/src/org/broad/igv/sam/reader/CachingQueryReader.java
index 271e250..1806fdc 100644
--- a/src/org/broad/igv/sam/reader/CachingQueryReader.java
+++ b/src/org/broad/igv/sam/reader/CachingQueryReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -21,43 +21,71 @@
  */
 package org.broad.igv.sam.reader;
 
-//~--- non-JDK imports --------------------------------------------------------
 
 import net.sf.samtools.SAMFileHeader;
 import net.sf.samtools.util.CloseableIterator;
 import org.apache.log4j.Logger;
 import org.broad.igv.PreferenceManager;
+import org.broad.igv.exceptions.DataLoadException;
+import org.broad.igv.feature.Genome;
+import org.broad.igv.feature.GenomeManager;
+import org.broad.igv.feature.SequenceManager;
 import org.broad.igv.sam.Alignment;
+import org.broad.igv.sam.AlignmentBlock;
 import org.broad.igv.sam.EmptyAlignmentIterator;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.util.MessageUtils;
 import org.broad.igv.util.LRUCache;
+import org.broad.igv.util.RuntimeUtils;
 
+import javax.swing.*;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
+import java.lang.ref.WeakReference;
+import java.util.*;
 
 /**
- * A wrapper for SamTextReader that supports query by interval.
+ * A wrapper for an AlignmentQueryReader that caches query results
  *
  * @author jrobinso
  */
-public class CachingQueryReader implements AlignmentQueryReader {
+public class CachingQueryReader {
 
     private static Logger log = Logger.getLogger(CachingQueryReader.class);
+
+    private static Set<WeakReference<CachingQueryReader>> activeReaders = Collections.synchronizedSet(new HashSet());
+
+    private static void cancelReaders() {
+        for (WeakReference<CachingQueryReader> readerRef : activeReaders) {
+            CachingQueryReader reader = readerRef.get();
+            if (reader != null) {
+                reader.cancel = true;
+            }
+        }
+        log.debug("Readers canceled");
+        activeReaders.clear();
+    }
+
+
+    //private static final int LOW_MEMORY_THRESHOLD = 150000000;
+    private static final int KB = 1000;
+    private static final int MITOCHONDRIA_TILE_SIZE = 1000;
+    private static int DEFAULT_TILE_SIZE = 16 * KB;
+    private static int MAX_TILE_COUNT = 4;
+
     String cachedChr = "";
-    static int maxTileCount = 30;
-    private static int defaultTileSize = 8000;
-    private int tileSize = defaultTileSize;
+    private int tileSize = DEFAULT_TILE_SIZE;
     AlignmentQueryReader reader;
-    LRUCache<Integer, Tile> cache;
+    LRUCache<Integer, AlignmentTile> cache;
+    //private int maxReadCount = 500000;
+
+    private boolean cancel = false;
 
     public CachingQueryReader(AlignmentQueryReader reader) {
         this.reader = reader;
-        cache = new LRUCache(maxTileCount);
-
-        tileSize = Math.min(defaultTileSize,
-                (int) (PreferenceManager.getInstance().getSAMPreferences().getMaxVisibleRange() * 1000));
+        cache = new LRUCache(this, MAX_TILE_COUNT);
+        tileSize = Math.min(DEFAULT_TILE_SIZE,
+                (int) (PreferenceManager.getInstance().getSAMPreferences().getMaxVisibleRange() * KB));
 
     }
 
@@ -65,7 +93,11 @@ public class CachingQueryReader implements AlignmentQueryReader {
         reader.close();
     }
 
-    public SAMFileHeader getHeader() {
+    public Set<String> getSequenceNames() {
+        return reader.getSequenceNames();
+    }
+
+    public SAMFileHeader getHeader() throws IOException {
         return reader.getHeader();
     }
 
@@ -77,11 +109,11 @@ public class CachingQueryReader implements AlignmentQueryReader {
         return reader.hasIndex();
     }
 
-    public CloseableIterator<Alignment> query(String sequence, int start, int end, boolean contained) {
+    public CloseableIterator<Alignment> query(String sequence, int start, int end, List<AlignmentCounts> counts) {
 
         int startTile = (start + 1) / getTileSize(sequence);
         int endTile = end / getTileSize(sequence);    // <= inclusive
-        List<Tile> tiles = getTiles(sequence, startTile, endTile);
+        List<AlignmentTile> tiles = getTiles(sequence, startTile, endTile);
 
         if (tiles.size() == 0) {
             return EmptyAlignmentIterator.getInstance();
@@ -89,39 +121,38 @@ public class CachingQueryReader implements AlignmentQueryReader {
 
         // Count total # of records
         int recordCount = tiles.get(0).getOverlappingRecords().size();
-        for (Tile t : tiles) {
+        for (AlignmentTile t : tiles) {
             recordCount += t.getContainedRecords().size();
         }
 
         List<Alignment> alignments = new ArrayList(recordCount);
         alignments.addAll(tiles.get(0).getOverlappingRecords());
-        for (Tile t : tiles) {
+        for (AlignmentTile t : tiles) {
             alignments.addAll(t.getContainedRecords());
+            counts.add(t.getCounts());
         }
         return new TiledIterator(start, end, alignments);
     }
 
-    private List<Tile> getTiles(String seq, int startTile, int endTile) {
+    public List<AlignmentTile> getTiles(String seq, int startTile, int endTile) {
 
         if (!seq.equals(cachedChr)) {
-            cache = new LRUCache(maxTileCount);
+            cache.clear();
             cachedChr = seq;
         }
 
 
-        List<Tile> tiles = new ArrayList(endTile - startTile + 1);
-        List<Tile> tilesToLoad = new ArrayList(endTile - startTile + 1);
+        List<AlignmentTile> tiles = new ArrayList(endTile - startTile + 1);
+        List<AlignmentTile> tilesToLoad = new ArrayList(endTile - startTile + 1);
 
         int tileSize = getTileSize(seq);
         for (int t = startTile; t <= endTile; t++) {
-            Tile tile = cache.get(t);
+            AlignmentTile tile = cache.get(t);
 
             if (tile == null) {
                 int start = t * tileSize;
                 int end = start + tileSize;
-
-                tile = new Tile(t, start, end);
-                cache.put(t, tile);
+                tile = new AlignmentTile(seq, t, start, end);
             }
 
             tiles.add(tile);
@@ -129,7 +160,9 @@ public class CachingQueryReader implements AlignmentQueryReader {
             // The current tile is loaded,  load any preceding tiles we have pending
             if (tile.isLoaded()) {
                 if (tilesToLoad.size() > 0) {
-                    loadTiles(seq, tilesToLoad);
+                    if (!loadTiles(seq, tilesToLoad)) {
+                        return tiles;
+                    }
                 }
                 tilesToLoad.clear();
             } else {
@@ -144,7 +177,23 @@ public class CachingQueryReader implements AlignmentQueryReader {
         return tiles;
     }
 
-    private void loadTiles(String seq, List<Tile> tiles) {
+    private boolean loadTiles(String seq, List<AlignmentTile> tiles) {
+
+        assert (tiles.size() > 0);
+
+
+        final PreferenceManager.SAMPreferences prefs = PreferenceManager.getInstance().getSAMPreferences();
+        final int qualityThreshold = prefs.getQualityThreshold();
+        boolean filterFailedReads = prefs.isFilterFailedReads();
+        ReadGroupFilter filter = ReadGroupFilter.getFilter();
+        boolean showDuplicates = prefs.isShowDuplicates();
+        //maxReadCount = PreferenceManager.getInstance().getAsInt(PreferenceManager.SAM_MAX_READS);
+
+        if (log.isDebugEnabled()) {
+            int first = tiles.get(0).getTileNumber();
+            int end = tiles.get(tiles.size() - 1).getTileNumber();
+            log.debug("Loading tiles: " + first + "-" + end);
+        }
 
         int start = tiles.get(0).start;
         int end = tiles.get(tiles.size() - 1).end;
@@ -152,49 +201,68 @@ public class CachingQueryReader implements AlignmentQueryReader {
 
         //log.debug("Loading : " + start + " - " + end);
         int alignmentCount = 0;
-        //long t0 = System.currentTimeMillis();
         try {
+            activeReaders.add(new WeakReference(this));
             iter = reader.query(seq, start, end, false);
 
             int tileSize = getTileSize(seq);
             while (iter != null && iter.hasNext()) {
+
+                if (cancel) {
+                    return false;
+                }
+
                 Alignment record = iter.next();
 
+
+                if (!record.isMapped() ||
+                        (!showDuplicates && record.isDuplicate()) ||
+                        (filterFailedReads && record.isVendorFailedRead()) ||
+                        record.getMappingQuality() < qualityThreshold ||
+                        (filter != null && filter.filterAlignment(record))) {
+                    continue;
+                }
+
                 // Range of tile indeces that this alignment contributes to.
                 int aStart = record.getAlignmentStart();
                 int aEnd = record.getEnd();
                 int idx0 = Math.max(0, (aStart - start) / tileSize);
-                int idx1 = Math.min(tiles.size() - 1, (record.getEnd() - start) / tileSize);
+                int idx1 = Math.min(tiles.size() - 1, (aEnd - start) / tileSize);
 
                 // Loop over tiles this read overlaps
                 for (int i = idx0; i <= idx1; i++) {
-                    Tile t = tiles.get(i);
-
-                    if ((aStart >= t.start) && (aStart < t.end)) {
-                        t.containedRecords.add(record);
-                    } else if ((aEnd >= t.start) && (aStart < t.start)) {
-                        t.overlappingRecords.add(record);
-                    }
+                    AlignmentTile t = tiles.get(i);
+                    t.addRecord(record);
                 }
 
                 alignmentCount++;
                 if (alignmentCount % 1000 == 0) {
+                    if (cancel) return false;
                     IGVMainFrame.getInstance().setStatusBarMessage("Reads loaded: " + alignmentCount);
+                    if (checkMemory() == false) {
+                        cancelReaders();
+                        return false;
+                    }
                 }
             }
 
-            for (Tile t : tiles) {
+            for (AlignmentTile t : tiles) {
                 t.setLoaded(true);
+                cache.put(t.getTileNumber(), t);
             }
 
-            //double dt = (System.currentTimeMillis() - t0) / 1000.0;
-            //log.debug("Loaded " + alignmentCount + " in " + dt);
+            return true;
 
         } catch (Exception e) {
             log.error("Error loading alignment data", e);
+            throw new DataLoadException("", "Error: " + e.toString());
         }
 
         finally {
+            // reset cancel flag.  It doesn't matter how we got here,  the read is complete and this flag is reset
+            // for the next time
+            cancel = false;
+            activeReaders.remove(this);
             if (iter != null) {
                 iter.close();
             }
@@ -202,18 +270,113 @@ public class CachingQueryReader implements AlignmentQueryReader {
         }
     }
 
+    private static synchronized int confirmMaxReadCount(String msg) {
+        log.debug("Enter max read count");
+        return JOptionPane.showConfirmDialog(null, msg, msg, JOptionPane.YES_NO_OPTION);
+    }
+
+    private static synchronized boolean checkMemory() {
+        if (RuntimeUtils.getAvailableMemoryFraction() < 0.2) {
+            LRUCache.clearCaches();
+            System.gc();
+            if (RuntimeUtils.getAvailableMemoryFraction() < 0.2) {
+                String msg = "Memory is low, reading terminating.";
+                MessageUtils.showMessage(msg);
+                return false;
+            }
+        }
+        return true;
+    }
+
     /**
      * @return the tileSize
      */
     public int getTileSize(String chr) {
         if (chr.equals("M") || chr.equals("chrM") || chr.equals("MT") || chr.equals("chrMT")) {
-            return 1000;
+            return MITOCHONDRIA_TILE_SIZE;
         } else {
             return tileSize;
         }
     }
 
-    static class Tile {
+    public void clearCache() {
+        if (cache != null) cache.clear();
+    }
+
+
+    public class TiledIterator implements CloseableIterator<Alignment> {
+
+        Iterator<Alignment> currentSamIterator;
+        int end;
+        Alignment nextRecord;
+        int start;
+        List<Alignment> alignments;
+
+        TiledIterator(int start, int end, List<Alignment> alignments) {
+            this.alignments = alignments;
+            this.start = start;
+            this.end = end;
+            currentSamIterator = alignments.iterator();
+            advanceToFirstRecord();
+        }
+
+        public void close() {
+            // No-op
+        }
+
+        public boolean hasNext() {
+            return nextRecord != null;
+        }
+
+        public Alignment next() {
+            Alignment ret = nextRecord;
+
+            advanceToNextRecord();
+
+            return ret;
+        }
+
+        public void remove() {
+            // ignored
+        }
+
+        private void advanceToFirstRecord() {
+            advanceToNextRecord();
+        }
+
+        private void advanceToNextRecord() {
+            advance();
+
+            while ((nextRecord != null) && (nextRecord.getEnd() < start)) {
+                advance();
+            }
+        }
+
+        private void advance() {
+            if (currentSamIterator.hasNext()) {
+                nextRecord = currentSamIterator.next();
+                if (nextRecord.getAlignmentStart() > end) {
+                    nextRecord = null;
+                }
+            } else {
+                nextRecord = null;
+            }
+        }
+
+
+    }
+
+    /**
+     * Caches alignments and counts for the coverage plot.
+     * <p/>
+     * Notes:
+     * A "bucket" is a virtual container holding all the alignments with identical start position.  The concept
+     * is introduced to control the # of alignments we hold in memory for deep coverage regions.  In practice,
+     * little or no information is added by displaying more than ~50X coverage.  For an average alignment length L
+     * and coverage deptgh D we do not need to store more than D/L alignments at any given start position.
+     */
+
+    public static class AlignmentTile {
 
         private boolean loaded = false;
         private List<Alignment> containedRecords;
@@ -221,147 +384,492 @@ public class CachingQueryReader implements AlignmentQueryReader {
         private List<Alignment> overlappingRecords;
         private int start;
         private int tileNumber;
+        private AlignmentCounts counts;
 
-        Tile(int tileNumber, int start, int end) {
+        /**
+         * Maximum # of alignments to load with a given start position
+         */
+        int maxBucketSize;
+        private int lastStart = -1;
+        private List<Alignment> currentBucket;
+
+
+        AlignmentTile(String chr, int tileNumber, int start, int end) {
             this.tileNumber = tileNumber;
             this.start = start;
             this.end = end;
             containedRecords = new ArrayList(16000);
             overlappingRecords = new ArrayList();
+            this.counts = new AlignmentCounts(chr, start, end);
+
+            int maxDepth = PreferenceManager.getInstance().getSAMPreferences().getMaxLevels();
+
+            // TODO -- compute this value from the data 
+            maxBucketSize = (maxDepth / 10) + 1;
+            currentBucket = new ArrayList(5 * maxBucketSize);
         }
 
-        /**
-         * @return the tileNumber
-         */
         public int getTileNumber() {
             return tileNumber;
         }
 
-        /**
-         * @param tileNumber the tileNumber to set
-         */
-        public void setTileNumber(int tileNumber) {
-            this.tileNumber = tileNumber;
-        }
 
-        /**
-         * @return the start
-         */
         public int getStart() {
             return start;
         }
 
-        /**
-         * @param start the start to set
-         */
         public void setStart(int start) {
             this.start = start;
         }
 
-        /**
-         * @return the containedRecords
-         */
+        public void addRecord(Alignment record) {
+            if (lastStart != record.getStart()) {
+                emptyBucket();
+                lastStart = record.getStart();
+
+            }
+            currentBucket.add(record);
+            counts.incCounts(record);
+        }
+
+
+        private void emptyBucket() {
+            List<Alignment> sampledRecords = sampleCurrentBucket();
+            for (Alignment alignment : sampledRecords) {
+                int aStart = alignment.getAlignmentStart();
+                int aEnd = alignment.getEnd();
+                if ((aStart >= start) && (aStart < end)) {
+                    containedRecords.add(alignment);
+                } else if ((aEnd >= start) && (aStart < start)) {
+                    overlappingRecords.add(alignment);
+                }
+            }
+            currentBucket.clear();
+        }
+
+
+        private List<Alignment> sampleCurrentBucket() {
+            if (currentBucket.size() < maxBucketSize) {
+                return currentBucket;
+            }
+
+            Random rand = new Random(System.currentTimeMillis()); // would make this static to the class
+
+            List subsetList = new ArrayList(maxBucketSize);
+            for (int i = 0; i < maxBucketSize; i++) {
+                // be sure to use Vector.remove() or you may get the same item twice
+                subsetList.add(currentBucket.remove(rand.nextInt(currentBucket.size())));
+            }
+
+            return subsetList;
+        }
+
+
         public List<Alignment> getContainedRecords() {
             return containedRecords;
         }
 
-        /**
-         * @param containedRecords the containedRecords to set
-         */
-        public void setContainedRecords(List<Alignment> containedRecords) {
-            this.containedRecords = containedRecords;
-        }
 
-        /**
-         * @return the overlappingRecords
-         */
         public List<Alignment> getOverlappingRecords() {
             return overlappingRecords;
         }
 
-        /**
-         * @param overlappingRecords the overlappingRecords to set
-         */
-        public void setOverlappingRecords(List<Alignment> overlappingRecords) {
-            this.overlappingRecords = overlappingRecords;
-        }
-
-        /**
-         * @return the loaded
-         */
         public boolean isLoaded() {
             return loaded;
         }
 
-        /**
-         * @param loaded the loaded to set
-         */
         public void setLoaded(boolean loaded) {
             this.loaded = loaded;
+
+            // Empty any remaining alignments in the current bucket
+            emptyBucket();
+
         }
 
+        public AlignmentCounts getCounts() {
+            return counts;
+        }
     }
 
-    public class TiledIterator implements CloseableIterator<Alignment> {
+    public static class AlignmentCounts {
 
-        int tileIdx = 0;
-        Iterator<Alignment> currentSamIterator;
-        int end;
-        Alignment nextRecord;
+        String genomeId;
+        //String chr;
         int start;
-        List<Alignment> alignments;
-
-        TiledIterator(int start, int end, List<Alignment> alignments) {
-            this.alignments = alignments;
+        int end;
+        byte[] reference;
+        // counts
+        int[] posA;
+        int[] posT;
+        int[] posC;
+        int[] posG;
+        int[] posN;
+        int[] negA;
+        int[] negT;
+        int[] negC;
+        int[] negG;
+        int[] negN;
+        int[] qA;
+        int[] qT;
+        int[] qC;
+        int[] qG;
+        int[] qN;
+        int[] posTotal;
+        int[] negTotal;
+        private int[] totalQ;
+        private int maxCount = 0;
+
+        public AlignmentCounts(String chr, int start, int end) {
+
+            Genome genome = ViewContext.getInstance().getGenome();
+            this.genomeId = genome.getId();
+            String chrAlias = genome.getChromosomeAlias(chr);
             this.start = start;
             this.end = end;
-            currentSamIterator = alignments.iterator();
-            advanceToFirstRecord();
+            reference = SequenceManager.readSequence(this.genomeId, chrAlias, start, end);
+
+            int nPts = end - start;
+            posA = new int[nPts];
+            posT = new int[nPts];
+            posC = new int[nPts];
+            posG = new int[nPts];
+            posN = new int[nPts];
+            posTotal = new int[nPts];
+            negA = new int[nPts];
+            negT = new int[nPts];
+            negC = new int[nPts];
+            negG = new int[nPts];
+            negN = new int[nPts];
+            negTotal = new int[nPts];
+            qA = new int[nPts];
+            qT = new int[nPts];
+            qC = new int[nPts];
+            qG = new int[nPts];
+            qN = new int[nPts];
+            totalQ = new int[nPts];
         }
 
-        public void close() {
-            // No-op
+        public int getTotalCount(int pos) {
+            int offset = pos - start;
+            if (offset < 0 || offset >= posA.length) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Position out of range: " + pos + " (valid range - " + start + "-" + end);
+                }
+                return 0;
+            } else {
+                return posTotal[offset] + negTotal[offset];
+
+            }
         }
 
-        public boolean hasNext() {
-            return nextRecord != null;
+        public int getNegTotal(int pos) {
+            int offset = pos - start;
+            if (offset < 0 || offset >= posA.length) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Position out of range: " + pos + " (valid range - " + start + "-" + end);
+                }
+                return 0;
+            } else {
+                return negTotal[offset];
+
+            }
         }
 
-        public Alignment next() {
-            Alignment ret = nextRecord;
+        public int getPosTotal(int pos) {
+            int offset = pos - start;
+            if (offset < 0 || offset >= posA.length) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Position out of range: " + pos + " (valid range - " + start + "-" + end);
+                }
+                return 0;
+            } else {
+                return posTotal[offset];
 
-            advanceToNextRecord();
+            }
+        }
 
-            return ret;
+        public int getTotalQuality(int pos) {
+            int offset = pos - start;
+            if (offset < 0 || offset >= posA.length) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Position out of range: " + pos + " (valid range - " + start + "-" + end);
+                }
+                return 0;
+            } else {
+                return totalQ[offset];
+
+            }
         }
 
-        public void remove() {
-            // ignored
+        public int getAvgQuality(int pos) {
+            int count = getTotalCount(pos);
+            return count == 0 ? 0 : getTotalQuality(pos) / count;
         }
 
-        private void advanceToFirstRecord() {
-            advanceToNextRecord();
+        public byte getReference(int pos) {
+            if (reference == null) {
+                return 0;
+            }
+            int offset = pos - start;
+            if (offset < 0 || offset >= reference.length) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Position out of range: " + pos + " (valid range - " + start + "-" + end);
+                }
+                return 0;
+            } else {
+                return reference[offset];
+            }
         }
 
-        private void advanceToNextRecord() {
-            advance();
+        public int getCount(int pos, byte b) {
+            int offset = pos - start;
+            if (offset < 0 || offset >= posA.length) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Position out of range: " + pos + " (valid range - " + start + "-" + end);
+                }
+                return 0;
+            } else {
+                switch (b) {
+                    case 'a':
+                    case 'A':
+                        return posA[offset] + negA[offset];
+                    case 't':
+                    case 'T':
+                        return posT[offset] + negT[offset];
+                    case 'c':
+                    case 'C':
+                        return posC[offset] + negC[offset];
+                    case 'g':
+                    case 'G':
+                        return posG[offset] + negG[offset];
+                    case 'n':
+                    case 'N':
+                        return posN[offset] + negN[offset];
+                }
+                log.error("Unknown nucleotide: " + b);
+                return 0;
+            }
+        }
 
-            while ((nextRecord != null) && (nextRecord.getEnd() < start)) {
-                advance();
+        public int getNegCount(int pos, byte b) {
+            int offset = pos - start;
+            if (offset < 0 || offset >= posA.length) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Position out of range: " + pos + " (valid range - " + start + "-" + end);
+                }
+                return 0;
+            } else {
+                switch (b) {
+                    case 'a':
+                    case 'A':
+                        return negA[offset];
+                    case 't':
+                    case 'T':
+                        return negT[offset];
+                    case 'c':
+                    case 'C':
+                        return negC[offset];
+                    case 'g':
+                    case 'G':
+                        return negG[offset];
+                    case 'n':
+                    case 'N':
+                        return negN[offset];
+                }
+                log.error("Unknown nucleotide: " + b);
+                return 0;
             }
         }
 
-        private void advance() {
-            if (currentSamIterator.hasNext()) {
-                nextRecord = currentSamIterator.next();
-                if (nextRecord.getAlignmentStart() > end) {
-                    nextRecord = null;
+        public int getPosCount(int pos, byte b) {
+            int offset = pos - start;
+            if (offset < 0 || offset >= posA.length) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Position out of range: " + pos + " (valid range - " + start + "-" + end);
                 }
+                return 0;
             } else {
-                nextRecord = null;
+                switch (b) {
+                    case 'a':
+                    case 'A':
+                        return posA[offset];
+                    case 't':
+                    case 'T':
+                        return posT[offset];
+                    case 'c':
+                    case 'C':
+                        return posC[offset];
+                    case 'g':
+                    case 'G':
+                        return posG[offset];
+                    case 'n':
+                    case 'N':
+                        return posN[offset];
+                }
+                log.error("Unknown nucleotide: " + b);
+                return 0;
+            }
+        }
+
+        public int getQuality(int pos, byte b) {
+            int offset = pos - start;
+            if (offset < 0 || offset >= posA.length) {
+                log.error("Position out of range: " + pos + " (valid range - " + start + "-" + end);
+                return 0;
+            } else {
+                switch (b) {
+                    case 'a':
+                    case 'A':
+                        return qA[offset];
+                    case 't':
+                    case 'T':
+                        return qT[offset];
+                    case 'c':
+                    case 'C':
+                        return qC[offset];
+                    case 'g':
+                    case 'G':
+                        return qG[offset];
+                    case 'n':
+                    case 'N':
+                        return qN[offset];
+                }
+                log.error("Unknown nucleotide: " + posN);
+                return 0;
+            }
+        }
+
+        public int getAvgQuality(int pos, byte b) {
+            int count = getCount(pos, b);
+            return count == 0 ? 0 : getQuality(pos, b) / count;
+        }
+
+
+        // For alignments without blocks -- TODO refactor, this is ugly
+
+        void incCounts(Alignment alignment) {
+            int start = alignment.getAlignmentStart();
+            int end = alignment.getAlignmentEnd();
+
+            AlignmentBlock[] blocks = alignment.getAlignmentBlocks();
+            if (blocks != null) {
+                for (AlignmentBlock b : blocks) {
+                    // Don't count softclips
+                    if (!b.isSoftClipped())
+                        incCounts(b, alignment.isNegativeStrand());
+                }
+            } else {
+                for (int pos = start; pos < end; pos++) {
+                    byte q = 0;
+                    incCount(pos, (byte) 'n', q, alignment.isNegativeStrand());
+                }
             }
         }
+
+        private void incCounts(AlignmentBlock block, boolean isNegativeStrand) {
+            int start = block.getStart();
+            byte[] bases = block.getBases();
+            if (bases != null) {
+                for (int i = 0; i < bases.length; i++) {
+                    int pos = start + i;
+                    byte q = block.getQuality(i);
+                    // TODO -- handle "="
+                    byte n = bases[i];
+                    incCount(pos, n, q, isNegativeStrand);
+                }
+            }
+        }
+
+        private void incCount(int pos, byte b, byte q, boolean isNegativeStrand) {
+
+            int offset = pos - start;
+            if (offset > 0 && offset < posA.length) {
+                switch (b) {
+                    case 'a':
+                    case 'A':
+                        if (isNegativeStrand) {
+                            negA[offset] = negA[offset] + 1;
+                        } else {
+                            posA[offset] = posA[offset] + 1;
+                        }
+                        qA[offset] = qA[offset] + q;
+                        break;
+                    case 't':
+                    case 'T':
+                        if (isNegativeStrand) {
+                            negT[offset] = negT[offset] + 1;
+                        } else {
+                            posT[offset] = posT[offset] + 1;
+                        }
+                        qT[offset] = qT[offset] + q;
+                        break;
+                    case 'c':
+                    case 'C':
+                        if (isNegativeStrand) {
+                            negC[offset] = negC[offset] + 1;
+                        } else {
+                            posC[offset] = posC[offset] + 1;
+                        }
+                        qC[offset] = qC[offset] + q;
+                        break;
+                    case 'g':
+                    case 'G':
+                        if (isNegativeStrand) {
+                            negG[offset] = negG[offset] + 1;
+                        } else {
+                            posG[offset] = posG[offset] + 1;
+                        }
+                        qG[offset] = qG[offset] + q;
+                        break;
+                    case 'n':
+                    case 'N':
+                        if (isNegativeStrand) {
+                            negN[offset] = negN[offset] + 1;
+                        } else {
+                            posN[offset] = posN[offset] + 1;
+                        }
+                        qN[offset] = qN[offset] + q;
+
+                }
+                if (isNegativeStrand) {
+                    negTotal[offset] = negTotal[offset] + 1;
+                } else {
+                    posTotal[offset] = posTotal[offset] + 1;
+                }
+                totalQ[offset] = totalQ[offset] + q;
+
+                maxCount = Math.max(posTotal[offset] + negTotal[offset], maxCount);
+            }
+        }
+
+        /**
+         * @return the start
+         */
+        public int getStart() {
+            return start;
+        }
+
+        /**
+         * @return the end
+         */
+        public int getEnd() {
+            return end;
+        }
+
+        /**
+         * @return the totalQ
+         */
+        public int[] getTotalQ() {
+            return totalQ;
+        }
+
+        /**
+         * @return the maxCount
+         */
+        public int getMaxCount() {
+            return maxCount;
+        }
     }
 }
-//~ Formatted by Jindent --- http://www.jindent.com
+
 
diff --git a/src/org/broad/igv/sam/reader/DotAlignedCodec.java b/src/org/broad/igv/sam/reader/DotAlignedCodec.java
index 61c9994..c8e0366 100644
--- a/src/org/broad/igv/sam/reader/DotAlignedCodec.java
+++ b/src/org/broad/igv/sam/reader/DotAlignedCodec.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -53,7 +53,7 @@ public class DotAlignedCodec implements SortingCollection.Codec<DotAlignedAlignm
 
     public void encode(DotAlignedAlignment alignment) {
         try {
-            outputStream.writeUTF(alignment.getChromosome());
+            outputStream.writeUTF(alignment.getChr());
             outputStream.writeInt(alignment.getStart());
             outputStream.writeInt(alignment.getEnd());
             outputStream.writeBoolean(alignment.isNegativeStrand());
diff --git a/src/org/broad/igv/sam/reader/DotAlignedIndexer.java b/src/org/broad/igv/sam/reader/DotAlignedIndexer.java
index ae16fe5..85559b7 100644
--- a/src/org/broad/igv/sam/reader/DotAlignedIndexer.java
+++ b/src/org/broad/igv/sam/reader/DotAlignedIndexer.java
@@ -1,3 +1,21 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
 package org.broad.igv.sam.reader;
 
 import javax.swing.*;
@@ -5,11 +23,11 @@ import java.io.File;
 
 /**
  * Created by IntelliJ IDEA.
-* User: jrobinso
-* Date: Dec 6, 2009
-* Time: 7:42:49 PM
-* To change this template use File | Settings | File Templates.
-*/
+ * User: jrobinso
+ * Date: Dec 6, 2009
+ * Time: 7:42:49 PM
+ * To change this template use File | Settings | File Templates.
+ */
 public class DotAlignedIndexer extends AlignmentIndexer {
 
     int baseOffset = 1;
diff --git a/src/org/broad/igv/sam/reader/DotAlignedParser.java b/src/org/broad/igv/sam/reader/DotAlignedParser.java
index 2ce8d72..7432243 100644
--- a/src/org/broad/igv/sam/reader/DotAlignedParser.java
+++ b/src/org/broad/igv/sam/reader/DotAlignedParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,8 +23,8 @@
 package org.broad.igv.sam.reader;
 
 import net.sf.samtools.util.AsciiLineReader;
-import org.broad.igv.util.ParsingUtils;
 import org.broad.igv.sam.DotAlignedAlignment;
+import org.broad.igv.util.ParsingUtils;
 
 /**
  * @author jrobinso
@@ -36,7 +36,7 @@ public class DotAlignedParser implements AlignmentParser {
     private static int END_COLUMN = 2;
     private static int STRAND_COLUMN = 3;
     private static int NAME_COLUMN = -1;
-    private String[] fields = new String[STRAND_COLUMN + 1];
+    private String[] fields = new String[20];
     boolean bedFormat = false;
 
     public DotAlignedParser() {
@@ -52,38 +52,49 @@ public class DotAlignedParser implements AlignmentParser {
     }
 
     public DotAlignedAlignment readNextRecord(AsciiLineReader reader) {
-        String nextLine = reader.readLine();
-        if (nextLine == null) {
-            return null;
+        String nextLine;
+        while ((nextLine = reader.readLine()) != null) {
+            DotAlignedAlignment alignment = createAlignment(nextLine);
+            if (alignment != null) {
+                return alignment;
+            }
         }
-        return createAlignment(nextLine);
+        return null;
     }
 
     private DotAlignedAlignment createAlignment(String nextLine) {
 
 
-        int nTokens = ParsingUtils.split(nextLine, fields, '\t');
-        // TODO -- what to do if nTokens < 4?
+        try {
+            int nTokens = ParsingUtils.split(nextLine, fields, '\t');
+            if(nTokens <= END_COLUMN)  {
+                System.out.println("Skipping line: " + nextLine);
+                return null;
+            }
+
+            String chr = fields[0];
+            int start = Integer.parseInt(fields[START_COLUMN]);
+            int end = Integer.parseInt(fields[END_COLUMN]);
 
-        String chr = fields[0];
-        int start = Integer.parseInt(fields[START_COLUMN]);
-        int end = Integer.parseInt(fields[END_COLUMN]);
 
+            if (bedFormat) {
+                boolean isNegative = false;
+                String name = "";
+                if (nTokens > STRAND_COLUMN) {
+                    isNegative = fields[STRAND_COLUMN].equals("-");
+                }
+                if (nTokens > NAME_COLUMN) {
+                    name = fields[NAME_COLUMN];
+                }
+                return new DotAlignedAlignment(chr, start, end, isNegative, name);
+            } else {
 
-        if (bedFormat) {
-            boolean isNegative = false;
-            String name = "";
-            if (nTokens > STRAND_COLUMN) {
-                isNegative = fields[STRAND_COLUMN].equals("-");
-            }
-            if (nTokens > NAME_COLUMN) {
-                name = fields[NAME_COLUMN];
+                boolean isNegative = fields[STRAND_COLUMN].equals("-");
+                return new DotAlignedAlignment(chr, start, end, isNegative);
             }
-            return new DotAlignedAlignment(chr, start, end, isNegative, name);
-        } else {
-
-            boolean isNegative = fields[STRAND_COLUMN].equals("-");
-            return new DotAlignedAlignment(chr, start, end, isNegative);
+        } catch (NumberFormatException e) {
+            System.out.println("Skipping line: " + nextLine);
+            return null;
         }
     }
 }
diff --git a/src/org/broad/igv/sam/reader/FeatureIndex.java b/src/org/broad/igv/sam/reader/FeatureIndex.java
index 8b01a77..16ac924 100644
--- a/src/org/broad/igv/sam/reader/FeatureIndex.java
+++ b/src/org/broad/igv/sam/reader/FeatureIndex.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -19,8 +19,13 @@ package org.broad.igv.sam.reader;
 
 //~--- non-JDK imports --------------------------------------------------------
 
-import org.broad.igv.ui.IGVModel;
+import org.apache.log4j.Logger;
+import org.broad.igv.exceptions.DataLoadException;
 import org.broad.igv.feature.Genome;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.util.ParsingUtils;
+import org.broad.igv.util.ResourceLocator;
+import sun.management.Agent;
 
 import java.io.*;
 import java.util.*;
@@ -32,6 +37,7 @@ public class FeatureIndex {
 
     private int tileWidth;
     private Map<String, ChromosomeIndex> chrIndeces;
+    private Logger log = Logger.getLogger(FeatureIndex.class);
 
     /**
      * Constructs ...
@@ -47,12 +53,29 @@ public class FeatureIndex {
     /**
      */
     public FeatureIndex(File f) {
+        this(f.getAbsolutePath());
+    }
+
+    /**
+     */
+    public FeatureIndex(String path) {
+
+        InputStream is = null;
         try {
+            is = ParsingUtils.openInputStream(new ResourceLocator(path));
             chrIndeces = new LinkedHashMap();
-            read(f);
+            read(is);
         } catch (IOException ex) {
-            // TODO log properly
-            ex.printStackTrace();
+            log.error("Error reading index", ex);
+            throw new DataLoadException("Error reading index: " + ex.getMessage(), path);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+                }
+            }
         }
 
     }
@@ -140,48 +163,35 @@ public class FeatureIndex {
 
     /**
      * Read the index.  Translate the chromosome names to the current genome aliases, if any.
-     * @param f
+     *
      * @throws IOException
      */
-    private void read(File f) throws IOException {
+    private void read(InputStream is) throws IOException {
 
-        Genome genome = IGVModel.getInstance().getViewContext().getGenome();
+        DataInputStream dis = new DataInputStream(new BufferedInputStream(is));
 
-        DataInputStream dis = null;
-        byte[] buffer = null;
-        String chr = null;
+        tileWidth = dis.readInt();
         try {
-            int length = (int) f.length();
-            buffer = new byte[length];
-            dis = new DataInputStream(new FileInputStream(f));
-            dis.readFully(buffer);
-        } catch (Exception e) {
-            e.printStackTrace();
-        } finally {
-            dis.close();
-        }
-
-        if (buffer != null && buffer.length > 0) {
-            DataInputStream dis2 = new DataInputStream(new ByteArrayInputStream(buffer));
-
-            tileWidth = dis2.readInt();
-            while (dis2.available() != 0) {
-
-                chr = genome.getChromosomeAlias(dis2.readUTF());
-                int nTiles = dis2.readInt();
-                int longestFeature = dis2.readInt();
+            while (true) {
+                String chr = dis.readUTF();
+                int nTiles = dis.readInt();
+                int longestFeature = dis.readInt();
 
                 List<TileDef> tileDefs = new ArrayList(nTiles);
                 int tileNumber = 0;
-                while (dis2.available() != 0 && tileNumber < nTiles) {
-                    long pos = dis2.readLong();
-                    int count = dis2.readInt();
+                while (tileNumber < nTiles) {
+                    long pos = dis.readLong();
+                    int count = dis.readInt();
                     tileDefs.add(new TileDef(pos, count));
                     tileNumber++;
                 }
 
                 chrIndeces.put(chr, new ChromosomeIndex(longestFeature, tileDefs));
             }
+        } catch (EOFException e) {
+            // This is normal.  Unfortuantely we don't have a better way to test EOF for this stream
+        }  catch(UTFDataFormatException e) {
+            log.error("Error reading chromosome name. ", e);
         }
 
     }
diff --git a/src/org/broad/igv/sam/reader/GeraldIndexer.java b/src/org/broad/igv/sam/reader/GeraldIndexer.java
index 4990c34..4ca6c65 100644
--- a/src/org/broad/igv/sam/reader/GeraldIndexer.java
+++ b/src/org/broad/igv/sam/reader/GeraldIndexer.java
@@ -1,3 +1,21 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
 package org.broad.igv.sam.reader;
 
 import javax.swing.*;
@@ -5,11 +23,11 @@ import java.io.File;
 
 /**
  * Created by IntelliJ IDEA.
-* User: jrobinso
-* Date: Dec 6, 2009
-* Time: 7:42:29 PM
-* To change this template use File | Settings | File Templates.
-*/ /*
+ * User: jrobinso
+ * Date: Dec 6, 2009
+ * Time: 7:42:29 PM
+ * To change this template use File | Settings | File Templates.
+ */ /*
 *         String readChr = mapChromosome(fields[CHROMOSOME_COLUMN]);
 int alignmentStart = Integer.parseInt(fields[ALIGNMENT_START_COLUMN].trim()) - 1;
 */
diff --git a/src/org/broad/igv/sam/reader/GeraldParser.java b/src/org/broad/igv/sam/reader/GeraldParser.java
index 1cf6fce..de78e86 100644
--- a/src/org/broad/igv/sam/reader/GeraldParser.java
+++ b/src/org/broad/igv/sam/reader/GeraldParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -27,11 +27,11 @@ import edu.mit.broad.picard.util.ArrayUtil;
 import edu.mit.broad.picard.util.SequenceUtil;
 import net.sf.samtools.SAMRecord;
 import net.sf.samtools.util.AsciiLineReader;
-import org.broad.igv.IGVConstants;
 import org.broad.igv.feature.Genome;
 import org.broad.igv.sam.GeraldAlignment;
 import org.broad.igv.sam.ReadMate;
-import org.broad.igv.ui.IGVModel;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.ui.UIConstants;
 import org.broad.igv.util.ParsingUtils;
 
 import java.io.BufferedReader;
@@ -67,7 +67,7 @@ public class GeraldParser implements AlignmentParser {
     public GeraldParser()
 
     {
-        genome = IGVModel.getInstance().getViewContext().getGenome();
+        genome = ViewContext.getInstance().getGenome();
     }
 
     public GeraldAlignment readNextRecord(AsciiLineReader reader) {
@@ -158,7 +158,7 @@ public class GeraldParser implements AlignmentParser {
             loadChrMap();
         }
         String chr = seqChrMap.containsKey(seq) ? seqChrMap.get(seq) : seq;
-        return genome.getChromosomeAlias(chr);
+        return genome == null ? chr : genome.getChromosomeAlias(chr);
 
     }
 
@@ -167,7 +167,7 @@ public class GeraldParser implements AlignmentParser {
     // TODO -- move this ot a utility class,  "loadMap(map, file)"
     static synchronized void loadChrMap() {
         seqChrMap = new HashMap();
-        File samDir = new File(IGVConstants.DEFAULT_IGV_DIRECTORY, "sam");
+        File samDir = new File(UIConstants.getIgvDirectory(), "sam");
         if (samDir.exists()) {
             File mapFile = new File(samDir, "sequence.map");
             if (!mapFile.exists()) {
diff --git a/src/org/broad/igv/sam/reader/GeraldQueryReader.java b/src/org/broad/igv/sam/reader/GeraldQueryReader.java
index 4ef65fc..65fc57c 100644
--- a/src/org/broad/igv/sam/reader/GeraldQueryReader.java
+++ b/src/org/broad/igv/sam/reader/GeraldQueryReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -26,6 +26,7 @@ import net.sf.samtools.SAMFileHeader;
 import net.sf.samtools.util.AsciiLineReader;
 import net.sf.samtools.util.CloseableIterator;
 import org.apache.log4j.Logger;
+import org.broad.igv.exceptions.DataLoadException;
 import org.broad.igv.sam.Alignment;
 import org.broad.igv.sam.EmptyAlignmentIterator;
 
@@ -33,6 +34,7 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.util.Set;
 
 /**
  * A wrapper for SamTextReader that supports query by interval.
@@ -44,12 +46,16 @@ public class GeraldQueryReader implements AlignmentQueryReader {
     private static Logger log = Logger.getLogger(GeraldQueryReader.class);
     static int MAX_READ_LENGTH = 100;
     static int maxTileCount = 20;
-    File alignmentFile;
+    String alignmentFile;
     FeatureIndex featureIndex;
     FileInputStream is;
     AlignmentParser parser;
 
-    public GeraldQueryReader(File alignmentFile) {
+    public GeraldQueryReader(String alignmentFile) {
+        this(alignmentFile, true);
+    }
+
+    public GeraldQueryReader(String alignmentFile, boolean requireIndex) {
         this.alignmentFile = alignmentFile;
         parser = getParserFor(alignmentFile);
         try {
@@ -57,21 +63,22 @@ public class GeraldQueryReader implements AlignmentQueryReader {
         } catch (FileNotFoundException fileNotFoundException) {
             fileNotFoundException.printStackTrace();
         }
-    }
-
-    public GeraldQueryReader(File samFile, File indexFile) {
-        this.alignmentFile = samFile;
-        if (indexFile.exists()) {
-            featureIndex = new FeatureIndex(indexFile);
+        if (requireIndex) {
+            featureIndex = SamUtils.getIndexFor(alignmentFile);
+            if (featureIndex == null) {
+                throw new DataLoadException("Could not locate index file.", alignmentFile);
+            }
         }
     }
 
-    private static AlignmentParser getParserFor(File file) {
-        String fn = file.getName().toLowerCase();
-        if (fn.endsWith(".aligned")) {
+
+    private static AlignmentParser getParserFor(String fn) {
+        if (fn.endsWith(".aligned") || (fn.endsWith(".aligned.txt"))) {
             return new DotAlignedParser();
         } else if (fn.endsWith(".bedz") || fn.endsWith(".bed")) {
             return new DotAlignedParser(true);
+        } else if (fn.endsWith(".psl") || fn.endsWith(".psxl")) {
+            return new PSLAlignmentParser();
         } else {
             return new GeraldParser();
         }
@@ -81,6 +88,23 @@ public class GeraldQueryReader implements AlignmentQueryReader {
         return null;
     }
 
+
+    private FeatureIndex getIndex() {
+        if (featureIndex == null) {
+            featureIndex = SamUtils.getIndexFor(alignmentFile);
+        }
+        return featureIndex;
+    }
+
+    public Set<String> getSequenceNames() {
+        FeatureIndex idx = getIndex();
+        if (idx == null) {
+            return null;
+        } else {
+            return idx.getIndexedChromosomes();
+        }
+    }
+
     public CloseableIterator<Alignment> query(final String sequence, final int start, final int end, final boolean contained) {
 
         if (featureIndex == null) {
@@ -102,6 +126,7 @@ public class GeraldQueryReader implements AlignmentQueryReader {
     }
 
     // TODO -- implementation
+
     public boolean hasIndex() {
         return true;  //To change body of implemented methods use File | Settings | File Templates.
     }
@@ -151,7 +176,7 @@ public class GeraldQueryReader implements AlignmentQueryReader {
 
         private void advanceToFirstRecord() {
             while ((readNextRecord()) != null) {
-                if (!currentRecord.getChromosome().equals(chr)) {
+                if (!currentRecord.getChr().equals(chr)) {
                     break;
                 } else if ((contained && currentRecord.getStart() >= start) ||
                         (!contained && currentRecord.getEnd() >= start)) {
@@ -170,13 +195,13 @@ public class GeraldQueryReader implements AlignmentQueryReader {
 
         public boolean hasNext() {
 
-            // chr == null => iterator, not query.  Fix this
+            // TODO chr == null implies an iterator, not query.  Fix this
             if (chr == null) {
                 return currentRecord != null;
             }
 
             if (currentRecord == null ||
-                    !chr.equals(currentRecord.getChromosome())) {
+                    !chr.equals(currentRecord.getChr())) {
                 return false;
             } else {
                 return contained ? currentRecord.getEnd() <= end
@@ -220,14 +245,4 @@ public class GeraldQueryReader implements AlignmentQueryReader {
         }
     }
 
-
-    public static void main(String[] args) {
-        GeraldQueryReader reader = new GeraldQueryReader(new File("test.aligned"));
-
-        CloseableIterator<Alignment> iter = reader.iterator();
-
-        while (iter.hasNext()) {
-            System.out.println(iter.next().getChromosome());
-        }
-    }
 }
diff --git a/src/org/broad/igv/sam/reader/IndexLoadException.java b/src/org/broad/igv/sam/reader/IndexLoadException.java
index 970e8ed..3164296 100644
--- a/src/org/broad/igv/sam/reader/IndexLoadException.java
+++ b/src/org/broad/igv/sam/reader/IndexLoadException.java
@@ -1,3 +1,21 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
 package org.broad.igv.sam.reader;
 
 /**
diff --git a/src/org/broad/igv/sam/reader/IntervalIndex.java b/src/org/broad/igv/sam/reader/IntervalIndex.java
index 8df67ec..4fbbcab 100644
--- a/src/org/broad/igv/sam/reader/IntervalIndex.java
+++ b/src/org/broad/igv/sam/reader/IntervalIndex.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/sam/reader/PSLAlignmentParser.java b/src/org/broad/igv/sam/reader/PSLAlignmentParser.java
new file mode 100644
index 0000000..fc5d849
--- /dev/null
+++ b/src/org/broad/igv/sam/reader/PSLAlignmentParser.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.sam.reader;
+
+import net.sf.samtools.util.AsciiLineReader;
+import org.broad.igv.feature.BasicFeature;
+import org.broad.igv.sam.Alignment;
+import org.broad.igv.sam.FeatureWrappedAlignment;
+import org.broad.igv.track.tribble.PSLCodec;
+import org.broad.tribble.Feature;
+
+/**
+ * @author jrobinso
+ * @date Aug 5, 2010
+ */
+
+public class PSLAlignmentParser implements AlignmentParser {
+
+    PSLCodec codec;
+
+    public PSLAlignmentParser() {
+        codec = new PSLCodec();
+    }
+
+    public Alignment readNextRecord(AsciiLineReader reader) {
+
+        String nextLine;
+        while ((nextLine = reader.readLine()) != null) {
+
+            Feature f = codec.decode(nextLine);
+            if(f != null && f instanceof BasicFeature) {
+                return new FeatureWrappedAlignment((BasicFeature) f);
+            }
+        }
+        return null;
+
+    }
+}
diff --git a/src/org/broad/igv/sam/reader/ReadGroupFilter.java b/src/org/broad/igv/sam/reader/ReadGroupFilter.java
index 2339ae6..04f9737 100644
--- a/src/org/broad/igv/sam/reader/ReadGroupFilter.java
+++ b/src/org/broad/igv/sam/reader/ReadGroupFilter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -19,10 +19,10 @@
 package org.broad.igv.sam.reader;
 
 import org.broad.igv.PreferenceManager;
-import org.broad.igv.util.ParsingUtils;
 import org.broad.igv.sam.Alignment;
 import org.broad.igv.ui.util.MessageUtils;
 import org.broad.igv.util.AsciiLineReader;
+import org.broad.igv.util.ParsingUtils;
 import org.broad.igv.util.ResourceLocator;
 
 import java.io.IOException;
diff --git a/src/org/broad/igv/sam/reader/SAMFileGroup.java b/src/org/broad/igv/sam/reader/SAMFileGroup.java
index f53d742..863cfaf 100644
--- a/src/org/broad/igv/sam/reader/SAMFileGroup.java
+++ b/src/org/broad/igv/sam/reader/SAMFileGroup.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/sam/reader/SAMReaderTest.java b/src/org/broad/igv/sam/reader/SAMReaderTest.java
index d4290d8..5d1f3c6 100644
--- a/src/org/broad/igv/sam/reader/SAMReaderTest.java
+++ b/src/org/broad/igv/sam/reader/SAMReaderTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/sam/reader/SamIndexCreatorDialog.java b/src/org/broad/igv/sam/reader/SamIndexCreatorDialog.java
index 5df3b7c..6b1e1fb 100644
--- a/src/org/broad/igv/sam/reader/SamIndexCreatorDialog.java
+++ b/src/org/broad/igv/sam/reader/SamIndexCreatorDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -29,7 +29,7 @@
 package org.broad.igv.sam.reader;
 
 import com.jidesoft.utils.SwingWorker;
-import org.broad.igv.ui.GuiUtilities;
+import org.broad.igv.ui.util.MessageUtils;
 import org.broad.igv.ui.util.UIUtilities;
 
 import javax.swing.*;
@@ -74,7 +74,7 @@ public class SamIndexCreatorDialog extends javax.swing.JDialog {
             try {
                 return worker.get();
             } catch (Exception ex) {
-                UIUtilities.showMessageDialog(ex.getMessage());
+                MessageUtils.showMessage(ex.getMessage());
             }
             return null;
         }
@@ -95,7 +95,7 @@ public class SamIndexCreatorDialog extends javax.swing.JDialog {
 
         public void setTimeRemaining(long timeInMillis) {
             final int timeRemaining = (int) (timeInMillis / (60 * 1000));
-            GuiUtilities.invokeOnEventThread(new Runnable() {
+            UIUtilities.invokeOnEventThread(new Runnable() {
 
                 public void run() {
                     String txt = String.valueOf(timeRemaining) + " minutes";
diff --git a/src/org/broad/igv/sam/reader/SamIndexer.java b/src/org/broad/igv/sam/reader/SamIndexer.java
index 1201af6..e051b0e 100644
--- a/src/org/broad/igv/sam/reader/SamIndexer.java
+++ b/src/org/broad/igv/sam/reader/SamIndexer.java
@@ -1,3 +1,21 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
 package org.broad.igv.sam.reader;
 
 import javax.swing.*;
@@ -5,38 +23,38 @@ import java.io.File;
 
 /**
  * Created by IntelliJ IDEA.
-* User: jrobinso
-* Date: Dec 6, 2009
-* Time: 7:42:01 PM
-* To change this template use File | Settings | File Templates.
-*/
+ * User: jrobinso
+ * Date: Dec 6, 2009
+ * Time: 7:42:01 PM
+ * To change this template use File | Settings | File Templates.
+ */
 public class SamIndexer extends AlignmentIndexer {
 
-   final static int FLAG_COL = 1;
-   final static int READ_UNMAPPED_FLAG = 0x4;
-
-   public SamIndexer(File samFile, JProgressBar progressBar, SamIndexCreatorDialog.IndexWorker worker) {
-       super(samFile, progressBar, worker);
-   }
-
-   int getAlignmentStart(String[] fields) throws NumberFormatException {
-       // Get alignmetn start and verify file is sorted.
-       int alignmentStart = Integer.parseInt(fields[3].trim()) - 1;
-       return alignmentStart;
-   }
-
-   int getAlignmentLength(String[] fields) throws NumberFormatException {
-       String cigarString = fields[5];
-       return SamUtils.getPaddedReferenceLength(cigarString);
-   }
-
-   String getChromosome(String[] fields) {
-       return fields[2];
-   }
-
-   @Override
-   boolean isMapped(String[] fields) {
-       int flags = Integer.parseInt(fields[FLAG_COL]);
-       return (flags & READ_UNMAPPED_FLAG) == 0;
-   }
+    final static int FLAG_COL = 1;
+    final static int READ_UNMAPPED_FLAG = 0x4;
+
+    public SamIndexer(File samFile, JProgressBar progressBar, SamIndexCreatorDialog.IndexWorker worker) {
+        super(samFile, progressBar, worker);
+    }
+
+    int getAlignmentStart(String[] fields) throws NumberFormatException {
+        // Get alignmetn start and verify file is sorted.
+        int alignmentStart = Integer.parseInt(fields[3].trim()) - 1;
+        return alignmentStart;
+    }
+
+    int getAlignmentLength(String[] fields) throws NumberFormatException {
+        String cigarString = fields[5];
+        return SamUtils.getPaddedReferenceLength(cigarString);
+    }
+
+    String getChromosome(String[] fields) {
+        return fields[2];
+    }
+
+    @Override
+    boolean isMapped(String[] fields) {
+        int flags = Integer.parseInt(fields[FLAG_COL]);
+        return (flags & READ_UNMAPPED_FLAG) == 0;
+    }
 }
diff --git a/src/org/broad/igv/sam/reader/SamListReader.java b/src/org/broad/igv/sam/reader/SamListReader.java
index b7a4b0a..1080e99 100644
--- a/src/org/broad/igv/sam/reader/SamListReader.java
+++ b/src/org/broad/igv/sam/reader/SamListReader.java
@@ -1,13 +1,31 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
 package org.broad.igv.sam.reader;
 
 import net.sf.samtools.SAMFileHeader;
+import net.sf.samtools.SAMSequenceRecord;
 import net.sf.samtools.util.CloseableIterator;
 import org.broad.igv.sam.Alignment;
 import org.broad.igv.util.ResourceLocator;
 
 import java.io.IOException;
-import java.util.Map;
-import java.util.HashMap;
+import java.util.*;
 
 /**
  * Poorly named class.  Created to wrap a list of sam files (1 per chromosome) as a single logical file
@@ -38,7 +56,7 @@ public class SamListReader implements AlignmentQueryReader {
         }
         if (header == null) {
             // Any header will do, see if we have one
-            if(readers.size() == 0) {
+            if (readers.size() == 0) {
                 Map.Entry<String, ResourceLocator> tmp = locators.entrySet().iterator().next();
                 AlignmentQueryReader firstReader = SamQueryReaderFactory.getReader(tmp.getValue());
                 readers.put(tmp.getKey(), firstReader);
@@ -48,19 +66,35 @@ public class SamListReader implements AlignmentQueryReader {
         return header;
     }
 
+    public Set<String> getSequenceNames() {
+        SAMFileHeader header = getHeader();
+        if (header == null) {
+            return null;
+        }
+        Set<String> seqNames = new HashSet();
+        List<SAMSequenceRecord> records = header.getSequenceDictionary().getSequences();
+        if (records.size() > 0) {
+            for (SAMSequenceRecord rec : header.getSequenceDictionary().getSequences()) {
+                String chr = rec.getSequenceName();
+                seqNames.add(chr);
+            }
+        }
+        return seqNames;
+    }
+
     public CloseableIterator<Alignment> iterator() {
         return null;  //To change body of implemented methods use File | Settings | File Templates.
     }
 
     public CloseableIterator<Alignment> query(String chr, int start, int end, boolean contained) {
         AlignmentQueryReader reader = readers.get(chr);
-        if(reader == null) {
+        if (reader == null) {
             ResourceLocator locator = locators.get(chr);
-            if(locator == null) {
+            if (locator == null) {
                 // TODO -- return empty iterator?
                 return null;
             }
-            reader =  SamQueryReaderFactory.getReader(locator);
+            reader = SamQueryReaderFactory.getReader(locator);
         }
         return reader.query(chr, start, end, contained);
     }
diff --git a/src/org/broad/igv/sam/reader/SamQueryReader.java b/src/org/broad/igv/sam/reader/SamQueryReader.java
index 57387d3..528899d 100644
--- a/src/org/broad/igv/sam/reader/SamQueryReader.java
+++ b/src/org/broad/igv/sam/reader/SamQueryReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/sam/reader/SamQueryReaderFactory.java b/src/org/broad/igv/sam/reader/SamQueryReaderFactory.java
index f3bb62b..c6de75f 100644
--- a/src/org/broad/igv/sam/reader/SamQueryReaderFactory.java
+++ b/src/org/broad/igv/sam/reader/SamQueryReaderFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -18,9 +18,13 @@
 package org.broad.igv.sam.reader;
 
 import net.sf.samtools.SAMFileReader;
+import org.apache.log4j.Logger;
+import org.broad.igv.exceptions.DataLoadException;
 import org.broad.igv.util.ResourceLocator;
+import sun.management.Agent;
 
 import java.io.File;
+import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
 
@@ -28,61 +32,69 @@ import java.net.URL;
  * @author jrobinso
  */
 public class SamQueryReaderFactory {
+    private static Logger log = Logger.getLogger(SamQueryReaderFactory.class);
 
     static {
         SAMFileReader.setDefaultValidationStringency(SAMFileReader.ValidationStringency.SILENT);
     }
 
-    public static AlignmentQueryReader getReader(String path, boolean cache) {
-        return getReader(new ResourceLocator(path), cache);
+    public static AlignmentQueryReader getReader(String path, boolean cacheAndIndex) {
+        return getReader(new ResourceLocator(path), cacheAndIndex);
     }
 
     public static AlignmentQueryReader getReader(ResourceLocator locator) {
         return getReader(locator, true);
     }
 
-    public static AlignmentQueryReader getReader(ResourceLocator locator, boolean cache) {
+    public static AlignmentQueryReader getReader(ResourceLocator locator, boolean requireIndex) {
 
         String path = locator.getPath().toLowerCase();
 
-        //if (path.endsWith(".sam.group"))
-        //{
-        //    return new CachingQueryReader(new SamGroupQueryReader(locator));
-        //}
 
         AlignmentQueryReader reader = null;
 
-        if (locator.isLocal()) {
-            File samFile = new File(locator.getPath());
-            if (path.endsWith(".bam")) {
-                reader = new BAMQueryReader(samFile);
-            } else if (path.endsWith(".sam")) {
-                //return new SamInMemoryQueryReader(samFile);
-                reader = new SamQueryTextReader(samFile);
-            } else if (path.endsWith("sorted.txt") || path.endsWith(".aligned")
-                    || path.endsWith("bedz") || path.endsWith("bed")) {
-                reader = new GeraldQueryReader(samFile);
-            } else {
-                throw new RuntimeException("Unknown file type: " + locator.getPath());
+        String samFile = locator.getPath();
+        if (path.endsWith(".sam")) {
+            try {
+                reader = new SamQueryTextReader(samFile, requireIndex);
+            } catch (IOException e) {
+                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
             }
-        } else {
-            if (locator.getPath().startsWith("http")) {
+        } else if (path.endsWith("sorted.txt")
+                || path.endsWith(".aligned")
+                || path.endsWith(".aligned.txt")
+                || path.endsWith("bedz")
+                || path.endsWith("bed")
+                || path.endsWith("psl")
+                || path.endsWith("pslx")) {
+            reader = new GeraldQueryReader(samFile, requireIndex);
+        } else if (path.endsWith(".bam")) {
+            if (locator.isLocal()) {
+                reader = new BAMQueryReader(new File(samFile));
+            } else if (locator.getPath().toLowerCase().startsWith("http:") ||
+                    locator.getPath().toLowerCase().startsWith("https:") ||
+                    locator.getPath().toLowerCase().startsWith("ftp:")) {
                 try {
                     URL url = new URL(locator.getPath());
-                    reader = new BAMHttpQueryReader(url);
+                    reader = new BAMHttpQueryReader(url, requireIndex);
                 }
                 catch (MalformedURLException e) {
-                    // todo log
-                    e.printStackTrace();
-                    return null;
+                    log.error("", e);
+                    throw new DataLoadException("Error loading BAM file: " + e.toString(),
+                            locator.getPath());
 
                 }
-            } else {
-                reader = new SamRemoteQueryReader(locator);
             }
+            else {
+                reader = new BAMRemoteQueryReader(locator);
+            }
+        } else if (path.endsWith(".bam.list")) {
+            reader = new BAMRemoteQueryReader(locator);
+        }  else {
+            throw new RuntimeException("No reader found for alignment file: " + locator.getPath());
         }
 
-        return cache ? new CachingQueryReader(reader) : reader;
+        return  reader;
     }
 }
 
diff --git a/src/org/broad/igv/sam/reader/SamQueryTextReader.java b/src/org/broad/igv/sam/reader/SamQueryTextReader.java
index 8eb55c6..adc6c69 100644
--- a/src/org/broad/igv/sam/reader/SamQueryTextReader.java
+++ b/src/org/broad/igv/sam/reader/SamQueryTextReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -28,13 +28,19 @@ import net.sf.samtools.SAMFileReader.ValidationStringency;
 import net.sf.samtools.SAMRecord;
 import net.sf.samtools.util.CloseableIterator;
 import org.apache.log4j.Logger;
+import org.broad.igv.Globals;
+import org.broad.igv.exceptions.DataLoadException;
 import org.broad.igv.sam.Alignment;
 import org.broad.igv.sam.EmptyAlignmentIterator;
 import org.broad.igv.sam.SamAlignment;
+import org.broad.igv.util.FileUtils;
+import org.broad.igv.util.SeekableStream;
+import org.broad.igv.util.SeekableStreamFactory;
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.util.Set;
 
 /**
  * A wrapper for SamTextReader that supports query by interval.
@@ -44,20 +50,26 @@ import java.io.IOException;
 public class SamQueryTextReader implements AlignmentQueryReader {
 
     static Logger log = Logger.getLogger(SamQueryTextReader.class);
-    File samFile;
+    String samFile;
+    SeekableStream stream;
     FeatureIndex featureIndex;
-    FileInputStream is;
     SAMFileHeader header;
 
-    public SamQueryTextReader(File samFile) {
-        this.samFile = samFile;
-        loadHeader();
+
+    public SamQueryTextReader(String samFile) throws IOException {
+        this(samFile, true);
     }
 
-    public SamQueryTextReader(File samFile, File indexFile) {
+    public SamQueryTextReader(String samFile, boolean requireIndex) throws IOException {
         this.samFile = samFile;
-        if (indexFile.exists()) {
-            featureIndex = new FeatureIndex(indexFile);
+        stream = SeekableStreamFactory.getStreamFor(samFile);
+        loadHeader();
+
+        if (requireIndex) {
+            featureIndex = SamUtils.getIndexFor(samFile);
+            if (featureIndex == null) {
+                throw new DataLoadException("Could not locate index file.", samFile);
+            }
         }
     }
 
@@ -69,12 +81,12 @@ public class SamQueryTextReader implements AlignmentQueryReader {
     }
 
     private void loadHeader() {
-        SAMFileReader reader = new SAMFileReader(samFile);
+        SAMFileReader reader = new SAMFileReader(stream);
         header = reader.getFileHeader();
         reader.close();
     }
 
-    public CloseableIterator<Alignment> query(final String sequence, final int start, final int end, final boolean contained) {
+    public synchronized CloseableIterator<Alignment> query(final String sequence, final int start, final int end, final boolean contained) {
 
         if (featureIndex == null) {
             featureIndex = SamUtils.getIndexFor(samFile);
@@ -98,10 +110,10 @@ public class SamQueryTextReader implements AlignmentQueryReader {
 
             try {
                 // Skip to the start of the chromosome (approximate)
-                is = new FileInputStream(samFile);
-                is.getChannel().position(seekPos.getStartPosition());
-
-                SAMFileReader reader = new SAMFileReader(is);
+                stream.close();
+                stream = SeekableStreamFactory.getStreamFor(samFile);
+                stream.seek(seekPos.getStartPosition());
+                SAMFileReader reader = new SAMFileReader(stream);
                 reader.setValidationStringency(ValidationStringency.SILENT);
 
                 CloseableIterator<SAMRecord> iter = reader.iterator();
@@ -116,19 +128,38 @@ public class SamQueryTextReader implements AlignmentQueryReader {
 
     public boolean hasIndex() {
         if (featureIndex == null) {
-            featureIndex = SamUtils.getIndexFor(samFile);
+            getIndex();
         }
         return featureIndex != null;
     }
 
     public void close() throws IOException {
-        if (is != null) {
-            is.close();
+        if (stream != null) {
+            stream.close();
+            stream = null;
         }
     }
 
+
+    private FeatureIndex getIndex() {
+        if (featureIndex == null) {
+            featureIndex = SamUtils.getIndexFor(samFile);
+        }
+        return featureIndex;
+    }
+
+    public Set<String> getSequenceNames() {
+        FeatureIndex idx = getIndex();
+        if (idx == null) {
+            return null;
+        } else {
+            return idx.getIndexedChromosomes();
+        }
+
+    }
+
     public CloseableIterator<Alignment> iterator() {
-        SAMFileReader reader = new SAMFileReader(samFile);
+        SAMFileReader reader = new SAMFileReader(stream);
         reader.setValidationStringency(ValidationStringency.SILENT);
         CloseableIterator<SAMRecord> iter = reader.iterator();
         return new SAMQueryIterator(iter);
diff --git a/src/org/broad/igv/sam/reader/SamRemoteQueryReader.java b/src/org/broad/igv/sam/reader/SamRemoteQueryReader.java
deleted file mode 100644
index ecf0995..0000000
--- a/src/org/broad/igv/sam/reader/SamRemoteQueryReader.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.sam.reader;
-
-import net.sf.samtools.BAMRecordCodec;
-import net.sf.samtools.SAMFileHeader;
-import net.sf.samtools.SAMRecord;
-import net.sf.samtools.SAMTextHeaderCodec;
-import net.sf.samtools.util.AsciiLineReader;
-import net.sf.samtools.util.CloseableIterator;
-import net.sf.samtools.util.LineReader;
-import org.apache.log4j.Logger;
-import org.broad.igv.sam.Alignment;
-import org.broad.igv.sam.SamAlignment;
-import org.broad.igv.util.ResourceLocator;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.zip.GZIPInputStream;
-
-/**
- * @author jrobinso
- */
-public class SamRemoteQueryReader implements AlignmentQueryReader {
-
-    Logger log = Logger.getLogger(SamRemoteQueryReader.class);
-    String serverURL;
-    String file;
-    SAMFileHeader header;
-
-    public SamRemoteQueryReader(ResourceLocator locator) {
-        this.serverURL = locator.getServerURL();
-        this.file = locator.getPath();
-        loadHeader();
-    }
-
-    public void close() throws IOException {
-        // Nothing to do
-    }
-
-    public boolean hasIndex() {
-        // There is no server API to to check this, so we'll be optimisitic.
-        return true;
-    }
-
-    public CloseableIterator<Alignment> query(String chr, int start, int end, boolean contained) {
-        try {
-            URL url = new URL(serverURL + "?method=samQuery&samFile=" + file + "&chr=" +
-                    chr + "&start=" + start + "&end=" + end + "&contained=" + contained);
-            URLConnection connection = url.openConnection();
-            connection.setRequestProperty("Connection", "close");
-            return new RemoteQueryIterator(new GZIPInputStream(connection.getInputStream(), 8192));
-
-        } catch (IOException ex) {
-            log.error("Error opening file", ex);
-            throw new RuntimeException(ex);
-        }
-    }
-
-    public SAMFileHeader getHeader() {
-        if (header == null) {
-            loadHeader();
-        }
-        return header;
-    }
-
-    private void loadHeader() {
-        InputStream is = null;
-        try {
-            URL url = new URL(serverURL + "?method=samHeader&samFile=" + file);
-            URLConnection connection = url.openConnection();
-            connection.setRequestProperty("Connection", "close");
-            is = connection.getInputStream();
-
-            LineReader reader = new AsciiLineReader(is);
-            SAMTextHeaderCodec code = new SAMTextHeaderCodec();
-            header = code.decode(reader, null);
-
-        } catch (IOException ex) {
-            log.error("Error opening file", ex);
-            throw new RuntimeException(ex);
-        } finally {
-            if (is != null) {
-                try {
-                    is.close();
-                } catch (IOException ex) {
-                    log.error("Error closing url stream", ex);
-                }
-            }
-        }
-    }
-
-    public CloseableIterator<Alignment> iterator() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    class RemoteQueryIterator implements CloseableIterator<Alignment> {
-
-        InputStream inputStream;
-        SAMRecord nextRecord;
-        BAMRecordCodec codec;
-
-        public RemoteQueryIterator(InputStream is) {
-            this.inputStream = is;
-            codec = new BAMRecordCodec(header);
-            codec.setInputStream(is);
-            advance();
-        }
-
-        private void advance() {
-            nextRecord = codec.decode();
-        }
-
-        public void close() {
-            if (inputStream != null) {
-                try {
-                    inputStream.close();
-                    inputStream = null;
-                } catch (IOException ex) {
-                    log.error("Error closing sam record stream", ex);
-                }
-            }
-        }
-
-        public boolean hasNext() {
-            return nextRecord != null;
-        }
-
-        public SamAlignment next() {
-            SamAlignment ret = new SamAlignment(nextRecord);
-            advance();
-            return ret;
-        }
-
-        public void remove() {
-            // ignored
-        }
-
-        // Just in case caller forgot to close the iterator
-        @Override
-        protected void finalize() throws Throwable {
-            super.finalize();
-            if (inputStream != null) {
-                inputStream.close();
-            }
-        }
-    }
-}
diff --git a/src/org/broad/igv/sam/reader/SamUtils.java b/src/org/broad/igv/sam/reader/SamUtils.java
index eb707ce..147b268 100644
--- a/src/org/broad/igv/sam/reader/SamUtils.java
+++ b/src/org/broad/igv/sam/reader/SamUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -26,8 +26,8 @@ import net.sf.samtools.CigarElement;
 import net.sf.samtools.CigarOperator;
 import net.sf.samtools.SAMRecord;
 import net.sf.samtools.util.StringUtil;
-import org.broad.igv.IGVConstants;
 import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.UIConstants;
 import org.broad.igv.util.FileUtils;
 
 import java.io.File;
@@ -40,16 +40,25 @@ public class SamUtils {
     // 10 KB window
     private static int DEFAULT_TILE_WIDTH = 100000;
 
-    public static FeatureIndex getIndexFor(File samFile) {
+    public static FeatureIndex getIndexFor(String samPath) {
 
-        File idxFile = new File(samFile.getAbsolutePath() + ".sai");
-        if (!idxFile.exists()) {
-            idxFile = getUserIdxFile(samFile);
-        }
-        if (idxFile.exists() && idxFile.lastModified() > samFile.lastModified()) {
-            return new FeatureIndex(idxFile);
+        String idxPath = samPath + ".sai";
+
+        if (FileUtils.resourceExists(idxPath)) {
+            return new FeatureIndex(idxPath);
+        } else if (!FileUtils.isRemote(idxPath)) {
+            File idxFile = new File(idxPath);
+            File samFile = new File(samPath);
+            if (!idxFile.exists()) {
+                idxFile = getUserIdxFile(samFile);
+            }
+            if (idxFile.exists() && idxFile.lastModified() > samFile.lastModified()) {
+                return new FeatureIndex(idxFile);
+            } else {
+                return createIndexFor(samFile);
+            }
         } else {
-            return createIndexFor(samFile);
+            return null;
         }
     }
 
@@ -67,7 +76,7 @@ public class SamUtils {
 
     private static File getUserIdxFile(File samFile) {
         File idxFile;
-        File samDir = new File(IGVConstants.DEFAULT_IGV_DIRECTORY, "sam");
+        File samDir = new File(UIConstants.getIgvDirectory(), "sam");
         //Need the path information to distinguish like name indeces in separate
         // directories.
         idxFile = new File(samDir, samFile.getName() + "_" + samFile.getParent().hashCode() + ".sai");
@@ -115,9 +124,4 @@ public class SamUtils {
         return c >= ZERO_BYTE && c <= NINE_BYTE;
     }
 
-    
-    public static void main(String [] args) {
-        File f = new File("foo/sdfsdfsd");
-        System.out.println(f.getParent());
-    }
 }
diff --git a/src/org/broad/igv/sam/reader/UnsortedFileException.java b/src/org/broad/igv/sam/reader/UnsortedFileException.java
index 9824d88..91c1004 100644
--- a/src/org/broad/igv/sam/reader/UnsortedFileException.java
+++ b/src/org/broad/igv/sam/reader/UnsortedFileException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/sam/reader/WrappedIterator.java b/src/org/broad/igv/sam/reader/WrappedIterator.java
index dd77bb9..dd9a594 100644
--- a/src/org/broad/igv/sam/reader/WrappedIterator.java
+++ b/src/org/broad/igv/sam/reader/WrappedIterator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/scatterplot/ScatterData.java b/src/org/broad/igv/scatterplot/ScatterData.java
new file mode 100644
index 0000000..8a14515
--- /dev/null
+++ b/src/org/broad/igv/scatterplot/ScatterData.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.scatterplot;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * User: jrobinso
+ * Date: Jan 28, 2010
+ */
+
+public class ScatterData {
+
+    private int count;
+    private Map<String, double[]> dataMap;
+    private Map<String, String[]> symbolMap;
+    String[] samples;
+
+    public ScatterData(int count, String[] samples, Map<String, double[]> dataMap, Map<String, String[]> symbolMap) {
+        this.count = count;
+        this.samples = samples;
+        this.dataMap = dataMap;
+        this.symbolMap = symbolMap;
+    }
+
+    public int getCount() {
+        return count;
+    }
+
+    public List<String> getLabels() {
+        return new ArrayList(dataMap.keySet());
+    }
+
+    public double[] getData(String label) {
+        return dataMap.get(label);
+    }
+
+    public String[] getSymbols(String label) {
+        return symbolMap.get(label);
+    }
+
+    public String[] getSamples() {
+        return samples;
+    }
+
+    public static ScatterData getTestData() {
+
+        int count = 300;
+        double[] cnData = new double[count];
+        double[] exprData = new double[count];
+        double[] methData = new double[count];
+        String[] treated = new String[count];
+        String[] hyperMutated = new String[count];
+        String[] samples = new String[count];
+
+        for (int i = 0; i < count; i++) {
+            samples[i] = "Sample " + i;
+            cnData[i] = ((double) i / count) * 4 * (2 * Math.random());
+            exprData[i] = -2 + ((double) i / count) * 4 * (2 * Math.random());
+            methData[i] = 1 - i * Math.random() / count;
+            treated[i] = Math.random() > 0.5 ? "AA" : "B";
+            hyperMutated[i] = Math.random() > 0.9 ? "Y" : "N";
+
+            System.out.println(cnData[i] + "\t" + exprData[i] + "\t" + methData[i]);
+        }
+
+        Map<String, double[]> dataMap = new LinkedHashMap();
+        dataMap.put("Copy number", cnData);
+        dataMap.put("Expression", exprData);
+        dataMap.put("Methylation", methData);
+
+        Map<String, String[]> symbolMap = new LinkedHashMap();
+        symbolMap.put("Treated", treated);
+        symbolMap.put("Hyper mutatated", hyperMutated);
+
+        return new ScatterData(count, samples, dataMap, symbolMap);
+
+
+    }
+
+    public static void main(String[] args) {
+        ScatterData sd = getTestData();
+
+        for (String l : sd.getLabels()) {
+            System.out.println(l);
+        }
+    }
+
+}
diff --git a/src/org/broad/igv/session/Persistable.java b/src/org/broad/igv/session/Persistable.java
new file mode 100644
index 0000000..0808262
--- /dev/null
+++ b/src/org/broad/igv/session/Persistable.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.session;
+
+import java.util.Map;
+
+/**
+ * User: jrobinso
+ * Date: Feb 23, 2010
+ */
+public interface Persistable {
+
+    public Map<String, String> getPersistentState();
+
+    public void restorePersistentState(Map<String, String> values);
+}
diff --git a/src/org/broad/igv/session/Session.java b/src/org/broad/igv/session/Session.java
index f7427ed..1bd2b55 100644
--- a/src/org/broad/igv/session/Session.java
+++ b/src/org/broad/igv/session/Session.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,23 +22,53 @@
 package org.broad.igv.session;
 
 
+import org.apache.log4j.Logger;
+import org.broad.igv.PreferenceManager;
+import org.broad.igv.feature.Feature;
+import org.broad.igv.feature.FeatureDB;
+import org.broad.igv.feature.Genome;
+import org.broad.igv.renderer.ContinuousColorScale;
+import org.broad.igv.track.TrackType;
+import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.RegionOfInterest;
 import org.broad.igv.ui.TrackFilter;
+import org.broad.igv.ui.util.MessageUtils;
+
+import java.util.*;
 
 /**
  * @author eflakes
  */
 public class Session {
+    private static Logger log = Logger.getLogger(Session.class);
 
-    private static int versionNumber = 2;
+    private static int versionNumber = 3;
     private String filePath;
+    private String groupTracksBy;
+    private ViewContext viewContext = ViewContext.getInstance();
+    private TrackFilter filter;
+    private HashMap<String, String> preferences;
+    private HashMap<TrackType, ContinuousColorScale> colorScales;
 
     /**
-     * Constructs ...
-     *
-     * @param filePath
+     * Map of chromosome -> regions of interest
      */
+    private Map<String, Collection<RegionOfInterest>> regionsOfInterest;
+
+
     public Session(String filePath) {
+        log.debug("New session");
+
         this.filePath = filePath;
+        regionsOfInterest = new LinkedHashMap();
+        preferences = new HashMap();
+        colorScales = new HashMap();
+
+    }
+
+
+    public String getSessionVersion() {
+        return String.valueOf(versionNumber);
     }
 
     /**
@@ -48,63 +78,249 @@ public class Session {
         return filePath;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public String getSessionVersion() {
-        return String.valueOf(versionNumber);
+    public void setPreference(String key, String value) {
+        preferences.put(key, value);
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public String getLocus() {
-        return locusString;
+    public void setColorScale(TrackType trackType, ContinuousColorScale colorScale) {
+        colorScales.put(trackType, colorScale);
     }
 
-    String locusString;
+    public ContinuousColorScale getColorScale(TrackType trackType) {
+        if (colorScales.containsKey(trackType)) {
+            return colorScales.get(trackType);
+        } else {
+            return PreferenceManager.getInstance().getColorScale(trackType);
+        }
+    }
 
-    /**
-     * Method description
-     *
-     * @param locus
-     */
-    public void setLocus(String locus) {
+    public boolean getDisplayOverlayTracks() {
+        final String key = PreferenceManager.DISPLAY_OVERLAY_TRACKS_KEY;
+        if (preferences.containsKey(key)) {
+            try {
+                return Boolean.parseBoolean(preferences.get(key));
+            }
+            catch (Exception e) {
+                log.error("Error converting boolean preference " + key + "=" + preferences.get(key));
+            }
+        }
+        return PreferenceManager.getInstance().getDiplayOverlayTracks();
 
-        // IGVMainFrame.getInstance().setCurrentLocus(locus);
-        this.locusString = locus;
+    }
+
+    public boolean getColorOverlay() {
+        final String key = PreferenceManager.COLOR_OVERLAY_KEY;
+        if (preferences.containsKey(key)) {
+            try {
+                return Boolean.parseBoolean(preferences.get(key));
+            }
+            catch (Exception e) {
+                log.error("Error converting boolean preference " + key + "=" + preferences.get(key));
+            }
+        }
+        return PreferenceManager.getInstance().getColorOverlay();
+
+    }
+
+    public String getOverlayAttribute() {
+        final String key = PreferenceManager.OVERLAY_ATTRIBUTE_KEY;
+        if (preferences.containsKey(key)) {
+            return preferences.get(key);
+
+        }
+        return PreferenceManager.getInstance().getOverlayAttribute();
+    }
+
+    public String getTrackAttributeName() {
+        final String key = PreferenceManager.TRACK_ATTRIBUTE_NAME_KEY;
+        if (preferences.containsKey(key)) {
+            return preferences.get(key);
+
+        }
+        return PreferenceManager.getInstance().getTrackAttributeName();
+    }
+
+
+    public String getLocus() {
+        ViewContext.Range range = getViewContext().getCurrentRange();
+        String startStr = String.valueOf(range.getStart());
+        String endStr = String.valueOf(range.getEnd());
+        String position = range.getChr() + ":" + startStr + "-" + endStr;
+        return position;
+    }
+
+
+    public void setLocus(String locusString) {
+        try {
+            Locus locus = getLocus(locusString);
+            if (locus == null) {
+                getViewContext().setChrName(getViewContext().getHomeChr());
+            } else {
+                getViewContext().jumpTo(locus.chr, locus.start, locus.end);
+            }
+            viewContext.history.push(locusString);
+        }
+        catch (Exception e) {
+            MessageUtils.showMessage("Error setting locus string: " + locusString + " (" + e.toString() + ")");
+            log.error("Error setting locus string", e);
+        }
     }
 
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public TrackFilter getFilter() {
         return filter;
     }
 
-    /**
-     * Method description
-     *
-     * @param filter
-     */
-    TrackFilter filter;
+    public void setFilter(TrackFilter filter) {
+        this.filter = filter;
+    }
+
+    public String getGroupTracksBy() {
+        return groupTracksBy;
+    }
+
+    public void setGroupTracksBy(String groupTracksBy) {
+        this.groupTracksBy = groupTracksBy;
+    }
+
+    public ViewContext getViewContext() {
+        return viewContext;
+    }
+
+    public Collection<RegionOfInterest> getRegionsOfInterest(String chr) {
+        return regionsOfInterest.get(chr);
+    }
+
+    public Collection<RegionOfInterest> getAllRegionsOfInterest() {
+        ArrayList<RegionOfInterest> roiList = new ArrayList();
+        for (Collection<RegionOfInterest> roi : regionsOfInterest.values()) {
+            roiList.addAll(roi);
+        }
+        return roiList;
+    }
+
+    public void addRegionOfInterestWithNoListeners(RegionOfInterest roi) {
+        String chr = roi.getChr();
+        Collection<RegionOfInterest> roiList = regionsOfInterest.get(chr);
+        if (roiList == null) {
+            roiList = new ArrayList();
+            regionsOfInterest.put(chr, roiList);
+        }
+        roiList.add(roi);
+    }
+
+    public void clearRegionsOfInterest() {
+        if (regionsOfInterest != null) {
+            regionsOfInterest.clear();
+        }
+    }
+
+    public void setFilePath(String filePath) {
+        this.filePath = filePath;
+    }
+
+
+    static class Locus {
+        String chr;
+        int start;
+        int end;
+
+        Locus(String chr, int start, int end) {
+            this.chr = chr;
+            this.start = start;
+            this.end = end;
+        }
+    }
+
+    static Locus getLocus(String searchString) {
+
+        if (searchString != null) {
+            String chr;
+            int[] startEnd;
+            int colon = searchString.indexOf(":");
+
+            if (colon > 0) {
+
+                // The chromosome is that portion of the search string up to the colon.
+                chr = searchString.substring(0, colon);
+                String posString = searchString.substring(colon).replace(":", "");
+                startEnd = getStartEnd(posString);
+
+                if (startEnd != null) {
+                    return new Locus(chr, startEnd[0], startEnd[1]);
+                }
+            } else {
+
+                // No chromosome delimiter (color),  The search string is either chromosome name
+                // or a locus in the current chromosome.
+                if (searchString.contains("-")) {
+
+                    // Presense of a dash indicates this is a locus string in the current chromosome
+                    startEnd = getStartEnd(searchString);
+                    if (startEnd != null) {
+                        return new Locus(null, startEnd[0], startEnd[1]);
+                    }
+                } else {
+                    Feature feature = FeatureDB.getFeature(searchString.toUpperCase().trim());
+                    if (feature != null) {
+                        return new Locus(feature.getChr(), feature.getStart(), feature.getEnd());
+                    } else {
+
+                        Genome genome = IGVMainFrame.getInstance().getViewContext().getGenome();
+                        if (genome.getChromosome(searchString) != null) {
+                            // No dash, this is either a chromosome or an unkown search string
+                            return new Locus(searchString, -1, -1);
+                        } else {
+                            return null;
+                        }
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
 
     /**
-     * Method description
-     *
-     * @param filter
+     * Return the start and end positions as a 2 element array for the input
+     * position string.  UCSC conventions  are followed for coordinates,
+     * specifically the internal representation is "zero" based (first base is
+     * numbered 0) but the display representation is "one" based (first base is
+     * numbered 1).   Consequently 1 is substracted from the parsed positions
      */
-    public void setFilter(TrackFilter filter) {
-        this.filter = filter;
+    private static int[] getStartEnd(String posString) {
+        try {
+            String[] posTokens = posString.split("-");
+            String startString = posTokens[0].replaceAll(",", "");
+            int start = Math.max(0, Integer.parseInt(startString)) - 1;
+
+            // Default value for end
+
+            int end = start + 1;
+            if (posTokens.length > 1) {
+                String endString = posTokens[1].replaceAll(",", "");
+                end = Integer.parseInt(endString);
+            }
+
+            if (posTokens.length == 1 || (end - start) < 10) {
+                int center = (start + end) / 2;
+                start = center - 20;
+                end = center + 20;
+            } else {
+                String endString = posTokens[1].replaceAll(",", "");
+
+                // Add 1 bp to end position t make it "inclusive"
+                end = Integer.parseInt(endString);
+            }
+
+            return new int[]{Math.min(start, end), Math.max(start, end)};
+        } catch (NumberFormatException numberFormatException) {
+            return null;
+        }
 
-        // IGVMainFrame.getInstance().setTrackFilter(filter);
     }
 
+
 }
+
+
diff --git a/src/org/broad/igv/session/SessionManager.java b/src/org/broad/igv/session/SessionManager.java
deleted file mode 100644
index 765277e..0000000
--- a/src/org/broad/igv/session/SessionManager.java
+++ /dev/null
@@ -1,699 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-package org.broad.igv.session;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import org.apache.log4j.Logger;
-import org.broad.igv.PreferenceManager;
-import org.broad.igv.track.Track;
-import org.broad.igv.track.TrackManager;
-import org.broad.igv.track.TrackType;
-import org.broad.igv.ui.*;
-import org.broad.igv.util.FilterElement.BooleanOperator;
-import org.broad.igv.util.FilterElement.Operator;
-import org.broad.igv.util.ResourceLocator;
-import org.w3c.dom.*;
-import org.xml.sax.SAXException;
-
-import javax.swing.*;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.*;
-
-/**
- *
- */
-public class SessionManager {
-
-    private static Logger log = Logger.getLogger(SessionManager.class);
-    private static String INPUT_FILE_KEY = "INPUT_FILE_KEY";
-    // Temporary values used in processing
-    private Collection<String> hiddenAttributes = null;
-    private Collection<ResourceLocator> dataFiles;
-    private Collection<ResourceLocator> missingDataFiles;
-    private static Map<String, String> attributeSynonymMap = new HashMap();
-    List<String> trackIds = new ArrayList();
-
-
-    static {
-        attributeSynonymMap.put("DATA FILE", "DATA SET");
-        attributeSynonymMap.put("TRACK NAME", "NAME");
-    }
-
-    /**
-     * Session Element types
-     */
-    public static enum SessionElement {
-
-        PANEL("Panel"),
-        TRACK("Track"),
-        COLOR_SCALE("ColorScale"),
-        COLOR_SCALES("ColorScales"),
-        DATA_TRACK("DataTrack"),
-        DATA_TRACKS("DataTracks"),
-        FEATURE_TRACK("FeatureTrack"),
-        FEATURE_TRACKS("FeatureTracks"),
-        DATA_FILE("DataFile"),
-        FILES("Files"),
-        FILTER_ELEMENT("FilterElement"),
-        FILTER("Filter"),
-        GLOBAL("Global"),
-        REGION("Region"),
-        REGIONS("Regions"),
-        VISIBLE_ATTRIBUTE("VisibleAttribute"),
-        VISIBLE_ATTRIBUTES("VisibleAttributes"),
-        HIDDEN_ATTRIBUTE("HiddenAttribute"),
-        HIDDEN_ATTRIBUTES("HidenAttributes");
-        private String name;
-
-        SessionElement(String name) {
-            this.name = name;
-        }
-
-        /**
-         * Method description
-         *
-         * @return
-         */
-        public String getText() {
-            return name;
-        }
-
-        /**
-         * Method description
-         *
-         * @return
-         */
-        @Override
-        public String toString() {
-            return getText();
-        }
-
-        /**
-         * Method description
-         *
-         * @param value
-         * @return
-         */
-        static public SessionElement findEnum(String value) {
-
-            if (value == null) {
-                return null;
-            } else {
-                return SessionElement.valueOf(value);
-            }
-        }
-    }
-
-    /**
-     * Session Attribute types
-     */
-    public static enum SessionAttribute {
-
-        BOOLEAN_OPERATOR("booleanOperator"),
-        COLOR("color"),
-        CHROMOSOME("chromosome"),
-        TYPE("type"),
-        DESCRIPTION("description"),
-        END_INDEX("end"),
-        EXPAND("expand"),
-        FILTER_MATCH("match"),
-        FILTER_SHOW_ALL_TRACKS("showTracks"),
-        GENOME("genome"),
-        GROUP_TRACKS_BY("groupTracksBy"),
-        HEIGHT("height"),
-        ID("id"),
-        ITEM("item"),
-        LOCUS("locus"),
-        NAME("name"),
-        RESOURCE_TYPE("resourceType"),
-        OPERATOR("operator"),
-        RELATIVE_PATH("relativePath"),
-        RENDERER("renderer"),
-        SCALE("scale"),
-        SERVER_URL("serverURL"),
-        PATH("path"),
-        SHOW_ATTRIBUTES("showAttributes"),
-        START_INDEX("start"),
-        VALUE("value"),
-        VERSION("version"),
-        VISIBLE("visible"),
-        WINDOW_FUNCTION("windowFunction"),
-        DISPLAY_NAME("displayName"),
-        COLOR_SCALE("colorScale");
-
-
-        private String name;
-
-        SessionAttribute(String name) {
-            this.name = name;
-        }
-
-        /**
-         * Method description
-         *
-         * @return
-         */
-        public String getText() {
-            return name;
-        }
-
-        /**
-         * Method description
-         *
-         * @return
-         */
-        @Override
-        public String toString() {
-            return getText();
-        }
-
-        /**
-         * Method description
-         *
-         * @param value
-         * @return
-         */
-        static public SessionAttribute findEnum(String value) {
-
-            if (value == null) {
-                return null;
-            } else {
-                return SessionAttribute.valueOf(value);
-            }
-        }
-    }
-
-    /**
-     * Instance method for the Session Manager.
-     */
-    private static SessionManager instance = new SessionManager();
-    ;
-
-    private SessionManager() {
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public static SessionManager getInstance() {
-
-
-        return instance;
-    }
-
-
-    /**
-     * Load a session from a file
-     *
-     * @param inputStream
-     * @param session
-     * @param resourceName
-     * @throws RuntimeException
-     */
-    boolean merge = false;
-
-    public void loadSession(InputStream inputStream, Session session, Object resourceName, boolean merge)
-            throws RuntimeException {
-
-        this.merge = merge;
-
-        Document document = null;
-        try {
-            document = createDOMDocumentFromXmlFile(inputStream);
-        } catch (Exception e) {
-            log.error("Session Management Error", e);
-            throw new RuntimeException(e);
-        }
-
-        HashMap additionalInformation = new HashMap();
-        additionalInformation.put(INPUT_FILE_KEY, resourceName);
-
-        processRootNode(session,
-                document.getElementsByTagName(SessionElement.GLOBAL.getText()).item(0),
-                additionalInformation);
-        if (session.getFilter() == null) {
-            TrackManager.getInstance().sortAllTracksByPosition(trackIds);
-        } else {
-            IGVMainFrame.getInstance().setTrackFilter(session.getFilter());
-        }
-    }
-
-    private void processRootNode(Session session, Node element, HashMap additionalInformation) {
-
-        if ((element == null) || (session == null)) {
-            return;
-        }
-
-        String nodeName = element.getNodeName();
-        if (!nodeName.equalsIgnoreCase(SessionElement.GLOBAL.getText())) {
-            throw new RuntimeException(element + " is not the root of the xml!");
-        }
-        process(session, element, additionalInformation);
-    }
-
-    private void processGlobal(Session session, Element element, HashMap additionalInformation) {
-
-        IGVMainFrame.getInstance().selectGenomeFromList(getAttribute(element, SessionAttribute.GENOME.getText()));
-        session.setLocus(getAttribute(element, SessionAttribute.LOCUS.getText()));
-        TrackManager.getInstance().setGroupByAttribute(getAttribute(element, SessionAttribute.GROUP_TRACKS_BY.getText()));
-
-        NodeList elements = element.getChildNodes();
-        process(session, elements, additionalInformation);
-
-        // IGVModel.getInstance().getViewContext().invalidateLocationScale();
-    }
-
-    /**
-     * Process the Files element.
-     * <p/>
-     * The RELATIVE_PATH attribute specifies whether file paths are relative
-     * or absolute.
-     *
-     * @param session
-     * @param element
-     */
-    private void processFiles(Session session, Element element, HashMap additionalInformation) {
-
-        dataFiles = new ArrayList();
-        missingDataFiles = new ArrayList();
-        NodeList elements = element.getChildNodes();
-        process(session, elements, additionalInformation);
-
-        if (missingDataFiles.size() > 0) {
-            StringBuffer message = new StringBuffer();
-            message.append("<html>The following data file(s) could not be located.<ul>");
-            for (ResourceLocator file : missingDataFiles) {
-                if (file.isLocal()) {
-                    message.append("<li>");
-                    message.append(file.getPath());
-                    message.append("</li>");
-                } else {
-                    message.append("<li>Server: ");
-                    message.append(file.getServerURL());
-                    message.append("  Path: ");
-                    message.append(file.getPath());
-                    message.append("</li>");
-                }
-            }
-            message.append("</ul>");
-            message.append("Common reasons for this include: ");
-            message.append("<ul><li>The session or data files have been moved.</li> ");
-            message.append(
-                    "<li>The data files are located on a drive that is not currently accessible.</li></ul>");
-            message.append("</html>");
-
-            JOptionPane.showMessageDialog(IGVMainFrame.getInstance(), message);
-        }
-        if (dataFiles.size() > 0) {
-            // TODO -- load the tracks into a tmp collection, process elements to make updates,
-            // then set permanently,
-
-            try {
-                TrackManager.getInstance().setRestoringSession(true);
-                TrackManager.getInstance().loadResources(dataFiles, merge);
-            }
-            finally {
-                TrackManager.getInstance().setRestoringSession(false);
-            }
-        }
-        dataFiles = null;
-    }
-
-    /**
-     * Process the data file element.  If relativePaths == true treat the
-     * file path as relative to the session file path.  If false
-     *
-     * @param session
-     * @param element
-     */
-    private void processDataFile(Session session, Element element, HashMap additionalInformation) {
-
-        ResourceLocator resourceLocator = null;
-        String serverURL = getAttribute(element, SessionAttribute.SERVER_URL.getText());
-        String filePath = getAttribute(element, SessionAttribute.NAME.getText());
-
-        // e.g. DAS
-        String resourceType = getAttribute(element, SessionAttribute.RESOURCE_TYPE.getText());
-
-        // If file is local
-        if ((serverURL == null || serverURL.trim().equals("")) &&
-                !filePath.startsWith("http:") && !filePath.startsWith("file:")) {
-            String relPathValue = getAttribute(element, SessionAttribute.RELATIVE_PATH.getText());
-            boolean relativePaths = ((relPathValue != null) && relPathValue.equalsIgnoreCase("true"));
-            File parent = (relativePaths ? new File(session.getPath()).getParentFile() : null);
-            File file = new File(parent, filePath);
-            resourceLocator = new ResourceLocator(file.getAbsolutePath());
-        } else {    // ...else must be from Server
-            resourceLocator = new ResourceLocator(serverURL, filePath);
-        }
-
-        if(resourceType != null) {
-            resourceLocator.setType(resourceType);
-        }
-
-        if (resourceLocator.exists()) {
-            dataFiles.add(resourceLocator);
-        } else {
-            missingDataFiles.add(resourceLocator);
-        }
-
-        NodeList elements = element.getChildNodes();
-        process(session, elements, additionalInformation);
-    }
-
-    private void processRegions(Session session, Element element, HashMap additionalInformation) {
-
-        IGVModel.getInstance().clearRegionsOfInterest();
-        NodeList elements = element.getChildNodes();
-        process(session, elements, additionalInformation);
-    }
-
-    private void processRegion(Session session, Element element, HashMap additionalInformation) {
-
-        String chromosome = getAttribute(element, SessionAttribute.CHROMOSOME.getText());
-        String start = getAttribute(element, SessionAttribute.START_INDEX.getText());
-        String end = getAttribute(element, SessionAttribute.END_INDEX.getText());
-        String description = getAttribute(element, SessionAttribute.DESCRIPTION.getText());
-
-        RegionOfInterest region = new RegionOfInterest(chromosome, new Integer(start),
-                new Integer(end), description);
-        IGVMainFrame.getInstance().addRegionOfInterest(region);
-
-        NodeList elements = element.getChildNodes();
-        process(session, elements, additionalInformation);
-    }
-
-    private void processFilter(Session session, Element element, HashMap additionalInformation) {
-
-        String match = getAttribute(element, SessionAttribute.FILTER_MATCH.getText());
-        String showAllTracks = getAttribute(element,
-                SessionAttribute.FILTER_SHOW_ALL_TRACKS.getText());
-
-        String filterName = getAttribute(element, SessionAttribute.NAME.getText());
-        TrackFilter filter = new TrackFilter(filterName, null);
-        additionalInformation.put(SessionElement.FILTER, filter);
-
-        NodeList elements = element.getChildNodes();
-        process(session, elements, additionalInformation);
-
-        // Save the filter
-        session.setFilter(filter);
-
-        // Set filter properties
-        if ("all".equalsIgnoreCase(match)) {
-            IGVMainFrame.getInstance().setFilterMatchAll(true);
-        } else if ("any".equalsIgnoreCase(match)) {
-            IGVMainFrame.getInstance().setFilterMatchAll(false);
-        }
-
-        if ("true".equalsIgnoreCase(showAllTracks)) {
-            IGVMainFrame.getInstance().setFilterShowAllTracks(true);
-        } else {
-            IGVMainFrame.getInstance().setFilterShowAllTracks(false);
-        }
-    }
-
-    private void processFilterElement(Session session, Element element,
-                                      HashMap additionalInformation) {
-
-        TrackFilter filter = (TrackFilter) additionalInformation.get(SessionElement.FILTER);
-        String item = getAttribute(element, SessionAttribute.ITEM.getText());
-        String operator = getAttribute(element, SessionAttribute.OPERATOR.getText());
-        String value = getAttribute(element, SessionAttribute.VALUE.getText());
-        String booleanOperator = getAttribute(element, SessionAttribute.BOOLEAN_OPERATOR.getText());
-
-        TrackFilterElement trackFilterElement = new TrackFilterElement(filter, item,
-                Operator.findEnum(operator), value,
-                BooleanOperator.findEnum(booleanOperator));
-        filter.add(trackFilterElement);
-
-        NodeList elements = element.getChildNodes();
-        process(session, elements, additionalInformation);
-    }
-
-    private void processHiddenAttributes(Session session, Element element,
-                                         HashMap additionalInformation) {
-
-        NodeList elements = element.getChildNodes();
-        process(session, elements, additionalInformation);
-    }
-
-    private void processHiddenAttribute(Session session,
-                                        Element element,
-                                        Collection<String> visibleAttributes,
-                                        HashMap additionalInformation) {
-
-        String trackAtributeName = getAttribute(element, SessionAttribute.NAME.getText());
-        visibleAttributes.add(trackAtributeName);
-
-        // See if this attribute has synoyms.  This was added to handle the
-        // situation of attribute header names changine between releases.  This
-        // is not a perfect solution, and the use of this map should be minimized.
-        if (attributeSynonymMap.containsKey(trackAtributeName)) {
-            visibleAttributes.add(attributeSynonymMap.get(trackAtributeName));
-        }
-
-        NodeList elements = element.getChildNodes();
-        process(session, elements, additionalInformation);
-    }
-
-    private void processPanel(Session session, Element element,
-                              HashMap additionalInformation) {
-        String nodeName = element.getNodeName();
-        String panelName = nodeName;
-        if (nodeName.equalsIgnoreCase(SessionElement.PANEL.getText())) {
-            panelName = element.getAttribute("name");
-        }
-
-        NodeList elements = element.getChildNodes();
-        for (int i = 0; i < elements.getLength(); i++) {
-            Node childNode = elements.item(i);
-            if (childNode.getNodeName().equalsIgnoreCase(SessionElement.DATA_TRACK.getText()) ||
-                    childNode.getNodeName().equalsIgnoreCase(SessionElement.TRACK.getText())) {
-                processTrack(session, (Element) childNode, additionalInformation, panelName);
-            } else {
-                process(session, childNode, additionalInformation);
-            }
-        }
-    }
-
-    private String getAttribute(Element element, String key) {
-
-        String value = element.getAttribute(key);
-        if (value != null) {
-            if (value.trim().equals("")) {
-                value = null;
-            }
-        }
-        return value;
-
-    }
-
-
-    private void processTrack(Session session, Element element,
-                              HashMap additionalInformation, String panelName) {
-
-        String id = getAttribute(element, SessionAttribute.ID.getText());
-
-        // TODo -- put in utility method, extacts attributes from element
-        HashMap<String, String> attributes = new HashMap();
-        NamedNodeMap nodeMap = element.getAttributes();
-        for (int i = 0; i < nodeMap.getLength(); i++) {
-            Node node = nodeMap.item(i);
-            String value = node.getNodeValue();
-            if (value != null && value.length() > 0) {
-                attributes.put(node.getNodeName(), value);
-            }
-        }
-
-        List<Track> matchedTracks = null;
-        matchedTracks = TrackManager.getInstance().findTracksById(id, TrackManager.MatchTrack.EXACT);
-        for (final Track track : matchedTracks) {
-            track.update(attributes);
-
-        }
-
-
-        trackIds.add(id);
-
-        NodeList elements = element.getChildNodes();
-        process(session, elements, additionalInformation);
-    }
-
-    private void processColorScales(Session session, Element element,
-                                    HashMap additionalInformation) {
-
-        NodeList elements = element.getChildNodes();
-        process(session, elements, additionalInformation);
-    }
-
-    private void processColorScale(Session session, Element element,
-                                   HashMap additionalInformation) {
-
-        String trackType = getAttribute(element, SessionAttribute.TYPE.getText());
-        String value = getAttribute(element, SessionAttribute.VALUE.getText());
-
-        setColorScaleSet(trackType, value);
-
-        NodeList elements = element.getChildNodes();
-        process(session, elements, additionalInformation);
-    }
-
-    /**
-     * Process a list of session element nodes.
-     *
-     * @param session
-     * @param elements
-     */
-    private void process(Session session, NodeList elements, HashMap additionalInformation) {
-        for (int i = 0; i < elements.getLength(); i++) {
-            Node childNode = elements.item(i);
-            process(session, childNode, additionalInformation);
-        }
-    }
-
-    /**
-     * Process a single session element node.
-     *
-     * @param session
-     * @param element
-     */
-    private void process(Session session, Node element, HashMap additionalInformation) {
-
-        if ((element == null) || (session == null)) {
-            return;
-        }
-
-        String nodeName = element.getNodeName();
-        if (true) {
-
-            if (nodeName.equalsIgnoreCase(SessionElement.GLOBAL.getText())) {
-                processGlobal(session, (Element) element, additionalInformation);
-            } else if (nodeName.equalsIgnoreCase(SessionElement.FILES.getText())) {
-                processFiles(session, (Element) element, additionalInformation);
-            } else if (nodeName.equalsIgnoreCase(SessionElement.DATA_FILE.getText())) {
-                processDataFile(session, (Element) element, additionalInformation);
-            } else if (nodeName.equalsIgnoreCase(SessionElement.REGIONS.getText())) {
-                processRegions(session, (Element) element, additionalInformation);
-            } else if (nodeName.equalsIgnoreCase(SessionElement.REGION.getText())) {
-                processRegion(session, (Element) element, additionalInformation);
-            } else if (nodeName.equalsIgnoreCase(SessionElement.FILTER.getText())) {
-                processFilter(session, (Element) element, additionalInformation);
-            } else if (nodeName.equalsIgnoreCase(SessionElement.FILTER_ELEMENT.getText())) {
-                processFilterElement(session, (Element) element, additionalInformation);
-            } else if (nodeName.equalsIgnoreCase(SessionElement.HIDDEN_ATTRIBUTES.getText())) {
-                hiddenAttributes = new ArrayList();
-                processHiddenAttributes(session, (Element) element, additionalInformation);
-                setHiddenAttributes(hiddenAttributes);
-                hiddenAttributes = null;
-            } else if (nodeName.equalsIgnoreCase(SessionElement.HIDDEN_ATTRIBUTE.getText())) {
-                processHiddenAttribute(session, (Element) element, hiddenAttributes,
-                        additionalInformation);
-            } else if (nodeName.equalsIgnoreCase(SessionElement.COLOR_SCALES.getText())) {
-                processColorScales(session, (Element) element, additionalInformation);
-            } else if (nodeName.equalsIgnoreCase(SessionElement.COLOR_SCALE.getText())) {
-                processColorScale(session, (Element) element, additionalInformation);
-            } else if (nodeName.equalsIgnoreCase(SessionElement.DATA_TRACKS.getText()) ||
-                    nodeName.equalsIgnoreCase(SessionElement.FEATURE_TRACKS.getText()) ||
-                    nodeName.equalsIgnoreCase(SessionElement.PANEL.getText())) {
-                processPanel(session, (Element) element, additionalInformation);
-
-            }
-
-        }
-    }
-
-    /**
-     * Reads an xml from an input file and creates DOM document.
-     *
-     * @param
-     * @return
-     * @throws ParserConfigurationException
-     * @throws IOException
-     * @throws SAXException
-     */
-    private Document createDOMDocumentFromXmlFile(InputStream inputStream)
-            throws ParserConfigurationException, IOException, SAXException {
-        DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
-        Document xmlDocument = documentBuilder.parse(inputStream);
-        return xmlDocument;
-    }
-
-
-    /**
-     * Method description
-     *
-     * @param hiddenAttributes
-     */
-    public void setHiddenAttributes(Collection<String> hiddenAttributes) {
-
-        if ((hiddenAttributes == null)) {
-            return;
-        }
-        IGVMainFrame.getInstance().setHiddenAttributes(hiddenAttributes);
-
-    }
-
-    public void setColorScaleSet(String type, String value) {
-
-        if (type == null | value == null) {
-            return;
-        }
-
-        TrackType trackType = TrackType.OTHER;
-
-        if (TrackType.ALLELE_SPECIFIC_COPY_NUMBER.name().equalsIgnoreCase(type)) {
-            trackType = TrackType.ALLELE_SPECIFIC_COPY_NUMBER;
-        } else if (TrackType.CHIP.name().equalsIgnoreCase(type)) {
-            trackType = TrackType.CHIP;
-        } else if (TrackType.COPY_NUMBER.name().equalsIgnoreCase(type)) {
-            trackType = TrackType.COPY_NUMBER;
-        } else if (TrackType.DNA_METHYLATION.name().equalsIgnoreCase(type)) {
-            trackType = TrackType.DNA_METHYLATION;
-        } else if (TrackType.OTHER.name().equalsIgnoreCase(type)) {
-            trackType = TrackType.OTHER;
-        } else if (TrackType.GENE_EXPRESSION.name().equalsIgnoreCase(type)) {
-            trackType = TrackType.GENE_EXPRESSION;
-        } else if (TrackType.LOH.name().equalsIgnoreCase(type)) {
-            trackType = TrackType.LOH;
-        } else if (TrackType.MUTATION.name().equalsIgnoreCase(type)) {
-            trackType = TrackType.MUTATION;
-        } else if (TrackType.PHASTCON.name().equalsIgnoreCase(type)) {
-            trackType = TrackType.PHASTCON;
-        } else if (TrackType.TILING_ARRAY.name().equalsIgnoreCase(type)) {
-            trackType = TrackType.TILING_ARRAY;
-        }
-
-        // TODO -- we really shouldn't be overriding user preferences here but currently
-        // its the only way to change a color scale.
-        // ColorScale colorScale = ColorScaleFactory.getScaleFromString(value);
-        PreferenceManager.getInstance().setColorScaleString(trackType, value);
-
-        // ColorScaleFactory.setColorScale(trackType, colorScale);
-    }
-
-
-}
diff --git a/src/org/broad/igv/session/SessionReader.java b/src/org/broad/igv/session/SessionReader.java
new file mode 100644
index 0000000..0e8f713
--- /dev/null
+++ b/src/org/broad/igv/session/SessionReader.java
@@ -0,0 +1,871 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+package org.broad.igv.session;
+
+import org.apache.log4j.Logger;
+import org.broad.igv.renderer.ColorScale;
+import org.broad.igv.renderer.ColorScaleFactory;
+import org.broad.igv.renderer.ContinuousColorScale;
+import org.broad.igv.renderer.DataRange;
+import org.broad.igv.track.AttributeManager;
+import org.broad.igv.track.Track;
+import org.broad.igv.track.TrackLoader;
+import org.broad.igv.track.TrackType;
+import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.RegionOfInterest;
+import org.broad.igv.ui.TrackFilter;
+import org.broad.igv.ui.TrackFilterElement;
+import org.broad.igv.ui.panel.TrackPanel;
+import org.broad.igv.ui.util.MessageUtils;
+import org.broad.igv.util.ColorUtilities;
+import org.broad.igv.util.FileUtils;
+import org.broad.igv.util.FilterElement.BooleanOperator;
+import org.broad.igv.util.FilterElement.Operator;
+import org.broad.igv.util.IGVHttpUtils;
+import org.broad.igv.util.ResourceLocator;
+import org.w3c.dom.*;
+import org.xml.sax.SAXException;
+
+import javax.swing.*;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.awt.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+import java.util.List;
+
+/**
+ *
+ */
+public class SessionReader {
+
+    private static Logger log = Logger.getLogger(SessionReader.class);
+    private static String INPUT_FILE_KEY = "INPUT_FILE_KEY";
+    // Temporary values used in processing
+
+    private Collection<ResourceLocator> dataFiles;
+    private Collection<ResourceLocator> missingDataFiles;
+    private static Map<String, String> attributeSynonymMap = new HashMap();
+    private boolean panelElementPresent = false;
+    private int version;
+
+
+    /**
+     * Map of track id -> track.  It is important to maintin the order in which tracks are added, thus
+     * the use of LinkedHashMap.
+     */
+    Map<String, List<Track>> trackDictionary = new LinkedHashMap();
+
+
+    static {
+        attributeSynonymMap.put("DATA FILE", "DATA SET");
+        attributeSynonymMap.put("TRACK NAME", "NAME");
+    }
+
+    /**
+     * Session Element types
+     */
+    public static enum SessionElement {
+
+        PANEL("Panel"),
+        TRACK("Track"),
+        COLOR_SCALE("ColorScale"),
+        COLOR_SCALES("ColorScales"),
+        DATA_TRACK("DataTrack"),
+        DATA_TRACKS("DataTracks"),
+        FEATURE_TRACKS("FeatureTracks"),
+        DATA_FILE("DataFile"),
+        RESOURCE("Resource"),
+        RESOURCES("Resources"),
+        FILES("Files"),
+        FILTER_ELEMENT("FilterElement"),
+        FILTER("Filter"),
+        SESSION("Session"),
+        GLOBAL("Global"),
+        REGION("Region"),
+        REGIONS("Regions"),
+        DATA_RANGE("DataRange"),
+        PREFERENCES("Preferences"),
+        PROPERTY("Property");
+
+        private String name;
+
+        SessionElement(String name) {
+            this.name = name;
+        }
+
+        /**
+         * Method description
+         *
+         * @return
+         */
+        public String getText() {
+            return name;
+        }
+
+        /**
+         * Method description
+         *
+         * @return
+         */
+        @Override
+        public String toString() {
+            return getText();
+        }
+
+        /**
+         * Method description
+         *
+         * @param value
+         * @return
+         */
+        static public SessionElement findEnum(String value) {
+
+            if (value == null) {
+                return null;
+            } else {
+                return SessionElement.valueOf(value);
+            }
+        }
+    }
+
+    /**
+     * Session Attribute types
+     */
+    public static enum SessionAttribute {
+
+        BOOLEAN_OPERATOR("booleanOperator"),
+        COLOR("color"),
+        CHROMOSOME("chromosome"),
+        END_INDEX("end"),
+        EXPAND("expand"),
+        FILTER_MATCH("match"),
+        FILTER_SHOW_ALL_TRACKS("showTracks"),
+        GENOME("genome"),
+        GROUP_TRACKS_BY("groupTracksBy"),
+        HEIGHT("height"),
+        ID("id"),
+        ITEM("item"),
+        LOCUS("locus"),
+        NAME("name"),
+        SAMPLE_ID("sampleID"),
+        RESOURCE_TYPE("resourceType"),
+        OPERATOR("operator"),
+        RELATIVE_PATH("relativePath"),
+        RENDERER("renderer"),
+        SCALE("scale"),
+        START_INDEX("start"),
+        VALUE("value"),
+        VERSION("version"),
+        VISIBLE("visible"),
+        WINDOW_FUNCTION("windowFunction"),
+        DISPLAY_NAME("displayName"),
+        COLOR_SCALE("colorScale"),
+
+        //RESOURCE ATTRIBUTES
+        PATH("path"),
+        LABEL("label"),
+        SERVER_URL("serverURL"),
+        HYPERLINK("hyperlink"),
+        INFOLINK("infolink"),
+        URL("url"),
+        FEATURE_URL("featureURL"),
+        DESCRIPTION("description"),
+        TYPE("type"),
+        COVERAGE("coverage"),
+        TRACK_LINE("trackLine");
+
+        //TODO Add the following into the Attributes
+        /*
+        boolean shadeBases;
+        boolean shadeCenters;
+        boolean flagUnmappedPairs;
+        boolean showAllBases;
+        int insertSizeThreshold;
+        boolean colorByStrand;
+        boolean colorByAmpliconStrand;
+         */
+
+
+        private String name;
+
+        SessionAttribute(String name) {
+            this.name = name;
+        }
+
+        public String getText() {
+            return name;
+        }
+
+        @Override
+        public String toString() {
+            return getText();
+        }
+
+        static public SessionAttribute findEnum(String value) {
+
+            if (value == null) {
+                return null;
+            } else {
+                return SessionAttribute.valueOf(value);
+            }
+        }
+    }
+
+
+    /**
+     * @param inputStream
+     * @param sessionName
+     * @return
+     * @throws RuntimeException
+     */
+
+    public Session loadSession(InputStream inputStream, Session session, String sessionName)
+            throws RuntimeException {
+
+        log.debug("Load session");
+
+
+        Document document = null;
+        try {
+            document = createDOMDocumentFromXmlFile(inputStream);
+        } catch (Exception e) {
+            log.error("Session Management Error", e);
+            throw new RuntimeException(e);
+        }
+
+        HashMap additionalInformation = new HashMap();
+        additionalInformation.put(INPUT_FILE_KEY, sessionName);
+
+        NodeList nodes = document.getElementsByTagName(SessionElement.GLOBAL.getText());
+        if (nodes == null || nodes.getLength() == 0) {
+            nodes = document.getElementsByTagName(SessionElement.SESSION.getText());
+        }
+
+        processRootNode(session, nodes.item(0), additionalInformation);
+
+        // Add tracks not explicitly set in file.  It is legal to define sessions with the DataFile section only (no
+        // Panel or Track elements).
+        addLeftoverTracks(trackDictionary.values());
+
+        if (session.getGroupTracksBy() != null && session.getGroupTracksBy().length() > 0) {
+            IGVMainFrame.getInstance().getTrackManager().setGroupByAttribute(session.getGroupTracksBy());
+        }
+
+        IGVMainFrame.getInstance().getTrackManager().resetOverlayTracks();
+
+        return session;
+    }
+
+    //TODO Check to make sure tracks are not being created twice
+    //TODO -- DONT DO THIS FOR NEW SESSIONS
+
+    private void addLeftoverTracks(Collection<List<Track>> tmp) {
+        if (version < 3 || !panelElementPresent) {
+            for (List<Track> tracks : tmp) {
+                for (Track track : tracks) {
+                    TrackPanel group = IGVMainFrame.getInstance().getTrackManager().getPanelFor(new ResourceLocator(track.getSourceFile()));
+                    group.addTrack(track);
+                }
+            }
+        }
+    }
+
+    /**
+     * Process a single session element node.
+     *
+     * @param session
+     * @param element
+     */
+    private void process(Session session, Node element, HashMap additionalInformation) {
+
+        if ((element == null) || (session == null)) {
+            return;
+        }
+
+        String nodeName = element.getNodeName();
+        if (true) {
+
+            if (nodeName.equalsIgnoreCase(SessionElement.GLOBAL.getText()) ||
+                    nodeName.equalsIgnoreCase(SessionElement.SESSION.getText())) {
+                processGlobal(session, (Element) element, additionalInformation);
+            } else if (nodeName.equalsIgnoreCase(SessionElement.FILES.getText())) {
+                processFiles(session, (Element) element, additionalInformation);
+            } else if (nodeName.equalsIgnoreCase(SessionElement.DATA_FILE.getText())) {
+                processDataFile(session, (Element) element, additionalInformation);
+            } else if (nodeName.equalsIgnoreCase(SessionElement.RESOURCES.getText())) {
+                processResources(session, (Element) element, additionalInformation);
+            } else if (nodeName.equalsIgnoreCase(SessionElement.RESOURCE.getText())) {
+                processResource(session, (Element) element, additionalInformation);
+            } else if (nodeName.equalsIgnoreCase(SessionElement.REGIONS.getText())) {
+                processRegions(session, (Element) element, additionalInformation);
+            } else if (nodeName.equalsIgnoreCase(SessionElement.REGION.getText())) {
+                processRegion(session, (Element) element, additionalInformation);
+            } else if (nodeName.equalsIgnoreCase(SessionElement.FILTER.getText())) {
+                processFilter(session, (Element) element, additionalInformation);
+            } else if (nodeName.equalsIgnoreCase(SessionElement.FILTER_ELEMENT.getText())) {
+                processFilterElement(session, (Element) element, additionalInformation);
+            } else if (nodeName.equalsIgnoreCase(SessionElement.COLOR_SCALES.getText())) {
+                processColorScales(session, (Element) element, additionalInformation);
+            } else if (nodeName.equalsIgnoreCase(SessionElement.COLOR_SCALE.getText())) {
+                processColorScale(session, (Element) element, additionalInformation);
+            } else if (nodeName.equalsIgnoreCase(SessionElement.PREFERENCES.getText())) {
+                processPreferences(session, (Element) element, additionalInformation);
+            } else if (nodeName.equalsIgnoreCase(SessionElement.PROPERTY.getText())) {
+                processPreference(session, (Element) element, additionalInformation);
+            } else if (nodeName.equalsIgnoreCase(SessionElement.DATA_TRACKS.getText()) ||
+                    nodeName.equalsIgnoreCase(SessionElement.FEATURE_TRACKS.getText()) ||
+                    nodeName.equalsIgnoreCase(SessionElement.PANEL.getText())) {
+                processPanel(session, (Element) element, additionalInformation);
+            }
+        }
+    }
+
+    private void processRootNode(Session session, Node element, HashMap additionalInformation) {
+
+        if ((element == null) || (session == null)) {
+            return;
+        }
+
+        String nodeName = element.getNodeName();
+        if (!(nodeName.equalsIgnoreCase(SessionElement.GLOBAL.getText()) || nodeName.equalsIgnoreCase(SessionElement.SESSION.getText()))) {
+            MessageUtils.showMessage("Session files must begin with a \"Global\" or \"Session\" element.  Found: " + nodeName);
+        }
+        process(session, element, additionalInformation);
+    }
+
+    private void processGlobal(Session session, Element element, HashMap additionalInformation) {
+
+        IGVMainFrame.getInstance().selectGenomeFromList(getAttribute(element, SessionAttribute.GENOME.getText()));
+        session.setLocus(getAttribute(element, SessionAttribute.LOCUS.getText()));
+        session.setGroupTracksBy(getAttribute(element, SessionAttribute.GROUP_TRACKS_BY.getText()));
+        String versionString = getAttribute(element, SessionAttribute.VERSION.getText());
+        try {
+            version = Integer.parseInt(versionString);
+        }
+        catch (NumberFormatException e) {
+            log.error("Non integer version number in session file: " + versionString);
+        }
+        NodeList elements = element.getChildNodes();
+        process(session, elements, additionalInformation);
+
+        // ViewContext.getInstance().invalidateLocationScale();
+    }
+
+    /**
+     * Process the Files element.
+     * <p/>
+     * The RELATIVE_PATH attribute specifies whether file paths are relative
+     * or absolute.
+     *
+     * @param session
+     * @param element
+     */
+    private void processFiles(Session session, Element element, HashMap additionalInformation) {
+
+        dataFiles = new ArrayList();
+        missingDataFiles = new ArrayList();
+        NodeList elements = element.getChildNodes();
+        process(session, elements, additionalInformation);
+
+        if (missingDataFiles.size() > 0) {
+            StringBuffer message = new StringBuffer();
+            message.append("<html>The following data file(s) could not be located.<ul>");
+            for (ResourceLocator file : missingDataFiles) {
+                if (file.isLocal()) {
+                    message.append("<li>");
+                    message.append(file.getPath());
+                    message.append("</li>");
+                } else {
+                    message.append("<li>Server: ");
+                    message.append(file.getServerURL());
+                    message.append("  Path: ");
+                    message.append(file.getPath());
+                    message.append("</li>");
+                }
+            }
+            message.append("</ul>");
+            message.append("Common reasons for this include: ");
+            message.append("<ul><li>The session or data files have been moved.</li> ");
+            message.append("<li>The data files are located on a drive that is not currently accessible.</li></ul>");
+            message.append("</html>");
+
+            MessageUtils.showMessage(message.toString());
+        }
+        if (dataFiles.size() > 0) {
+
+            TrackLoader loader = new TrackLoader();
+            for (ResourceLocator locator : dataFiles) {
+                //TODO Go through the tracks and reassign them to the resourceLocator
+                try {
+                    List<Track> tracks = loader.load(locator);
+                    if (tracks.size() > 0) {
+                        for (Track track : tracks) {
+
+                            String id = version < 3 ? track.getId_142() : track.getId();
+
+                            List<Track> trackList = trackDictionary.get(id);
+                            if (trackList == null) {
+                                trackList = new ArrayList();
+                                trackDictionary.put(id, trackList);
+
+                            }
+                            trackList.add(track);
+
+                            track.setAttributeValue("DATA FILE", locator.getPath());
+                            track.setAttributeValue("DATA TYPE", track.getTrackType().toString());
+
+                        }
+                    } else {
+                        AttributeManager.getInstance().loadSampleInfo(locator);
+                    }
+                } catch (Exception e) {
+                    log.error("Error loading " + locator.getPath(), e);
+                    MessageUtils.showMessage("<html>Error loading: " + locator.getPath() + "<br>" + e.toString());
+                }
+            }
+
+            // TODO -- sample info file
+
+        }
+        dataFiles = null;
+    }
+
+    /**
+     * @param session
+     * @param element
+     * @Deprecated // TODO -- this method should not be called.  Delete?
+     * @Deprectated -- user processResource
+     * Process the data file element.  If relativePaths == true treat the
+     * file path as relative to the session file path.  If false
+     */
+    private void processDataFile(Session session, Element element, HashMap additionalInformation) {
+
+        ResourceLocator resourceLocator = null;
+        String serverURL = getAttribute(element, SessionAttribute.SERVER_URL.getText());
+        String filePath = getAttribute(element, SessionAttribute.NAME.getText());
+
+        // e.g. DAS
+        String resourceType = getAttribute(element, SessionAttribute.RESOURCE_TYPE.getText());
+
+        // If file is local
+        if ((serverURL == null || serverURL.trim().equals("")) &&
+                !(filePath.startsWith("http:") || filePath.startsWith("https:") ||
+                        filePath.startsWith("file:"))) {
+            String relPathValue = getAttribute(element, SessionAttribute.RELATIVE_PATH.getText());
+            boolean relativePaths = ((relPathValue != null) && relPathValue.equalsIgnoreCase("true"));
+            File parent = (relativePaths ? new File(session.getPath()).getParentFile() : null);
+            File file = new File(parent, filePath);
+            resourceLocator = new ResourceLocator(file.getAbsolutePath());
+        } else {    // ...else must be from Server
+            resourceLocator = new ResourceLocator(serverURL, filePath);
+        }
+
+        if (resourceType != null) {
+            resourceLocator.setType(resourceType);
+        }
+
+        if (resourceLocator.exists()) {
+            dataFiles.add(resourceLocator);
+        } else {
+            missingDataFiles.add(resourceLocator);
+        }
+
+        NodeList elements = element.getChildNodes();
+        process(session, elements, additionalInformation);
+    }
+
+    private void processResources(Session session, Element element, HashMap additionalInformation) {
+        processFiles(session, element, additionalInformation);
+    }
+
+    private void processResource(Session session, Element element, HashMap additionalInformation) {
+
+        String label = getAttribute(element, SessionAttribute.LABEL.getText());
+        String name = getAttribute(element, SessionAttribute.NAME.getText());
+        String sampleId = getAttribute(element, SessionAttribute.SAMPLE_ID.getText());
+        String description = getAttribute(element, SessionAttribute.DESCRIPTION.getText());
+        String type = getAttribute(element, SessionAttribute.TYPE.getText());
+        String coverage = getAttribute(element, SessionAttribute.COVERAGE.getText());
+        String trackLine = getAttribute(element, SessionAttribute.TRACK_LINE.getText());
+        String colorString = getAttribute(element, SessionAttribute.COLOR.getText());
+
+        String relPathValue = getAttribute(element, SessionAttribute.RELATIVE_PATH.getText());
+        boolean relativePaths = ((relPathValue != null) && relPathValue.equalsIgnoreCase("true"));
+        String serverURL = getAttribute(element, SessionAttribute.SERVER_URL.getText());
+        String path = getAttribute(element, SessionAttribute.PATH.getText());
+
+        ResourceLocator resourceLocator = new ResourceLocator(serverURL, path);
+        if (relativePaths) {
+            if (FileUtils.isRemote(session.getPath())) {
+                int idx = session.getPath().lastIndexOf("/");
+                String basePath = session.getPath().substring(0, idx);
+                String resPath = basePath + "/" + path;
+                resourceLocator = new ResourceLocator(serverURL, resPath);
+            } else {
+                File parent = (relativePaths ? new File(session.getPath()).getParentFile() : null);
+                File file = new File(parent, path);
+                resourceLocator = new ResourceLocator(serverURL, file.getAbsolutePath());
+            }
+        }
+
+
+        String url = getAttribute(element, SessionAttribute.URL.getText());
+        if (url == null) {
+            url = getAttribute(element, SessionAttribute.FEATURE_URL.getText());
+        }
+        resourceLocator.setUrl(url);
+
+        String infolink = getAttribute(element, SessionAttribute.HYPERLINK.getText());
+        if (infolink == null) {
+            infolink = getAttribute(element, SessionAttribute.INFOLINK.getText());
+        }
+        resourceLocator.setInfolink(infolink);
+
+
+        // Label is deprecated in favor of name.
+        if (name != null) {
+            resourceLocator.setName(name);
+        } else {
+            resourceLocator.setName(label);
+        }
+
+        resourceLocator.setSampleId(sampleId);
+
+
+        resourceLocator.setDescription(description);
+        // This test added to get around earlier bug in the writer
+        if (type != null && !type.equals("local")) {
+            resourceLocator.setType(type);
+        }
+        resourceLocator.setCoverage(coverage);
+        resourceLocator.setTrackLine(trackLine);
+
+        if (colorString != null) {
+            try {
+                Color c = ColorUtilities.getColorFromString(colorString);
+                resourceLocator.setColor(c);
+            } catch (Exception e) {
+                log.error("Error setting color: ", e);
+            }
+        }
+
+        if (resourceLocator.exists()) {
+            dataFiles.add(resourceLocator);
+        } else {
+            missingDataFiles.add(resourceLocator);
+        }
+
+        NodeList elements = element.getChildNodes();
+        process(session, elements, additionalInformation);
+
+    }
+
+    private void processRegions(Session session, Element element, HashMap additionalInformation) {
+
+        session.clearRegionsOfInterest();
+        NodeList elements = element.getChildNodes();
+        process(session, elements, additionalInformation);
+    }
+
+    private void processRegion(Session session, Element element, HashMap additionalInformation) {
+
+        String chromosome = getAttribute(element, SessionAttribute.CHROMOSOME.getText());
+        String start = getAttribute(element, SessionAttribute.START_INDEX.getText());
+        String end = getAttribute(element, SessionAttribute.END_INDEX.getText());
+        String description = getAttribute(element, SessionAttribute.DESCRIPTION.getText());
+
+        RegionOfInterest region = new RegionOfInterest(chromosome, new Integer(start), new Integer(end), description);
+        IGVMainFrame.getInstance().addRegionOfInterest(region);
+
+        NodeList elements = element.getChildNodes();
+        process(session, elements, additionalInformation);
+    }
+
+    private void processFilter(Session session, Element element, HashMap additionalInformation) {
+
+        String match = getAttribute(element, SessionAttribute.FILTER_MATCH.getText());
+        String showAllTracks = getAttribute(element, SessionAttribute.FILTER_SHOW_ALL_TRACKS.getText());
+
+        String filterName = getAttribute(element, SessionAttribute.NAME.getText());
+        TrackFilter filter = new TrackFilter(filterName, null);
+        additionalInformation.put(SessionElement.FILTER, filter);
+
+        NodeList elements = element.getChildNodes();
+        process(session, elements, additionalInformation);
+
+        // Save the filter
+        session.setFilter(filter);
+
+        // Set filter properties
+        if ("all".equalsIgnoreCase(match)) {
+            IGVMainFrame.getInstance().setFilterMatchAll(true);
+        } else if ("any".equalsIgnoreCase(match)) {
+            IGVMainFrame.getInstance().setFilterMatchAll(false);
+        }
+
+        if ("true".equalsIgnoreCase(showAllTracks)) {
+            IGVMainFrame.getInstance().setFilterShowAllTracks(true);
+        } else {
+            IGVMainFrame.getInstance().setFilterShowAllTracks(false);
+        }
+    }
+
+    private void processFilterElement(Session session, Element element,
+                                      HashMap additionalInformation) {
+
+        TrackFilter filter = (TrackFilter) additionalInformation.get(SessionElement.FILTER);
+        String item = getAttribute(element, SessionAttribute.ITEM.getText());
+        String operator = getAttribute(element, SessionAttribute.OPERATOR.getText());
+        String value = getAttribute(element, SessionAttribute.VALUE.getText());
+        String booleanOperator = getAttribute(element, SessionAttribute.BOOLEAN_OPERATOR.getText());
+
+        TrackFilterElement trackFilterElement = new TrackFilterElement(filter, item,
+                Operator.findEnum(operator), value,
+                BooleanOperator.findEnum(booleanOperator));
+        filter.add(trackFilterElement);
+
+        NodeList elements = element.getChildNodes();
+        process(session, elements, additionalInformation);
+    }
+
+    private void processPanel(Session session, Element element, HashMap additionalInformation) {
+        panelElementPresent = true;
+        String nodeName = element.getNodeName();
+        String panelName = nodeName;
+        int height = 0;
+        int width = 0;
+        if (nodeName.equalsIgnoreCase(SessionElement.PANEL.getText())) {
+            panelName = element.getAttribute("name");
+            try {
+                height = Integer.valueOf(element.getAttribute("height"));
+                width = Integer.valueOf(element.getAttribute("width"));
+            } catch (NumberFormatException nfe) {
+            }
+        }
+
+        List<Track> panelTracks = new ArrayList();
+        NodeList elements = element.getChildNodes();
+        for (int i = 0; i < elements.getLength(); i++) {
+            Node childNode = elements.item(i);
+            if (childNode.getNodeName().equalsIgnoreCase(SessionElement.DATA_TRACK.getText()) ||
+                    childNode.getNodeName().equalsIgnoreCase(SessionElement.TRACK.getText())) {
+
+                List<Track> tracks = processTrack(session, (Element) childNode, additionalInformation);
+                if (tracks != null) {
+                    panelTracks.addAll(tracks);
+                }
+            } else {
+                process(session, childNode, additionalInformation);
+            }
+        }
+
+        TrackPanel panel = IGVMainFrame.getInstance().getDataPanel(panelName);
+        if (height == 0) {
+            height = panel.getPreferredPanelHeight();
+        }
+        if (width == 0) {
+            width = panel.getViewportWidth();
+        }
+        panel.setHeight(height);
+        panel.addTracks(panelTracks);
+    }
+
+
+    /**
+     * Process a track element.  This should return a single track, but could return multiple tracks since the
+     * uniqueness of the track id is not enforced.
+     *
+     * @param session
+     * @param element
+     * @param additionalInformation
+     * @return
+     */
+
+    private List<Track> processTrack(Session session, Element element, HashMap additionalInformation) {
+
+        String id = getAttribute(element, SessionAttribute.ID.getText());
+
+        // TODo -- put in utility method, extacts attributes from element **Definitely need to do this
+        HashMap<String, String> tAttributes = new HashMap();
+        HashMap<String, String> drAttributes = new HashMap();
+
+        NamedNodeMap tNodeMap = element.getAttributes();
+        for (int i = 0; i < tNodeMap.getLength(); i++) {
+            Node node = tNodeMap.item(i);
+            String value = node.getNodeValue();
+            if (value != null && value.length() > 0) {
+                tAttributes.put(node.getNodeName(), value);
+            }
+        }
+
+
+        if (element.hasChildNodes()) {
+            Node childNode = element.getFirstChild();
+            Node sibNode = childNode.getNextSibling();
+            NamedNodeMap drNodeMap = sibNode.getAttributes();
+
+            for (int i = 0; i < drNodeMap.getLength(); i++) {
+                Node node = drNodeMap.item(i);
+                String value = node.getNodeValue();
+                if (value != null && value.length() > 0) {
+                    drAttributes.put(node.getNodeName(), value);
+                }
+            }
+        }
+
+        // Get matching tracks.  The trackNameDictionary is used for pre V 2 files, where ID was loosely defined
+        List<Track> matchedTracks = trackDictionary.get(id);
+
+        if (matchedTracks == null) {
+            log.info("Warning.  No tracks were found with id: " + id + " in session file");
+        } else {
+            for (final Track track : matchedTracks) {
+                track.restorePersistentState(tAttributes);
+                DataRange dr = track.getDataRange();
+                dr.restorePersistentState(drAttributes);
+                track.setDataRange(dr);
+            }
+            trackDictionary.remove(id);
+        }
+
+        NodeList elements = element.getChildNodes();
+        process(session, elements, additionalInformation);
+
+        return matchedTracks;
+    }
+
+    private void processColorScales(Session session, Element element, HashMap additionalInformation) {
+
+        NodeList elements = element.getChildNodes();
+        process(session, elements, additionalInformation);
+    }
+
+    private void processColorScale(Session session, Element element, HashMap additionalInformation) {
+
+        String trackType = getAttribute(element, SessionAttribute.TYPE.getText());
+        String value = getAttribute(element, SessionAttribute.VALUE.getText());
+
+        setColorScaleSet(session, trackType, value);
+
+        NodeList elements = element.getChildNodes();
+        process(session, elements, additionalInformation);
+    }
+
+    private void processPreferences(Session session, Element element, HashMap additionalInformation) {
+
+        NodeList elements = element.getChildNodes();
+        process(session, elements, additionalInformation);
+    }
+
+    private void processPreference(Session session, Element element, HashMap additionalInformation) {
+
+        String name = getAttribute(element, SessionAttribute.NAME.getText());
+        String value = getAttribute(element, SessionAttribute.VALUE.getText());
+
+        session.setPreference(name, value);
+
+        NodeList elements = element.getChildNodes();
+        process(session, elements, additionalInformation);
+    }
+
+    /**
+     * Process a list of session element nodes.
+     *
+     * @param session
+     * @param elements
+     */
+    private void process(Session session, NodeList elements, HashMap additionalInformation) {
+        for (int i = 0; i < elements.getLength(); i++) {
+            Node childNode = elements.item(i);
+            process(session, childNode, additionalInformation);
+        }
+    }
+
+
+    /**
+     * Reads an xml from an input file and creates DOM document.
+     *
+     * @param
+     * @return
+     * @throws ParserConfigurationException
+     * @throws IOException
+     * @throws SAXException
+     */
+    private Document createDOMDocumentFromXmlFile(InputStream inputStream)
+            throws ParserConfigurationException, IOException, SAXException {
+        DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+        Document xmlDocument = documentBuilder.parse(inputStream);
+        return xmlDocument;
+    }
+
+
+    public void setColorScaleSet(Session session, String type, String value) {
+
+        if (type == null | value == null) {
+            return;
+        }
+
+        TrackType trackType = TrackType.OTHER;
+
+        if (TrackType.ALLELE_SPECIFIC_COPY_NUMBER.name().equalsIgnoreCase(type)) {
+            trackType = TrackType.ALLELE_SPECIFIC_COPY_NUMBER;
+        } else if (TrackType.CHIP.name().equalsIgnoreCase(type)) {
+            trackType = TrackType.CHIP;
+        } else if (TrackType.COPY_NUMBER.name().equalsIgnoreCase(type)) {
+            trackType = TrackType.COPY_NUMBER;
+        } else if (TrackType.DNA_METHYLATION.name().equalsIgnoreCase(type)) {
+            trackType = TrackType.DNA_METHYLATION;
+        } else if (TrackType.OTHER.name().equalsIgnoreCase(type)) {
+            trackType = TrackType.OTHER;
+        } else if (TrackType.GENE_EXPRESSION.name().equalsIgnoreCase(type)) {
+            trackType = TrackType.GENE_EXPRESSION;
+        } else if (TrackType.LOH.name().equalsIgnoreCase(type)) {
+            trackType = TrackType.LOH;
+        } else if (TrackType.MUTATION.name().equalsIgnoreCase(type)) {
+            trackType = TrackType.MUTATION;
+        } else if (TrackType.PHASTCON.name().equalsIgnoreCase(type)) {
+            trackType = TrackType.PHASTCON;
+        } else if (TrackType.TILING_ARRAY.name().equalsIgnoreCase(type)) {
+            trackType = TrackType.TILING_ARRAY;
+        }
+
+        // TODO -- refactor to remove instanceof / cast.  Currently only ContinuousColorScale is handled
+        ColorScale colorScale = ColorScaleFactory.getScaleFromString(value);
+        if (colorScale instanceof ContinuousColorScale) {
+            session.setColorScale(trackType, (ContinuousColorScale) colorScale);
+        }
+
+        // ColorScaleFactory.setColorScale(trackType, colorScale);
+    }
+
+    private String getAttribute(Element element, String key) {
+        String value = element.getAttribute(key);
+        if (value != null) {
+            if (value.trim().equals("")) {
+                value = null;
+            }
+        }
+        return value;
+    }
+
+
+}
diff --git a/src/org/broad/igv/session/SessionWriter.java b/src/org/broad/igv/session/SessionWriter.java
index 9eb4368..7d2e30f 100644
--- a/src/org/broad/igv/session/SessionWriter.java
+++ b/src/org/broad/igv/session/SessionWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -20,14 +20,19 @@ package org.broad.igv.session;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
-import org.broad.igv.session.SessionManager.SessionAttribute;
-import org.broad.igv.session.SessionManager.SessionElement;
+import org.broad.igv.renderer.DataRange;
+import org.broad.igv.session.SessionReader.SessionAttribute;
+import org.broad.igv.session.SessionReader.SessionElement;
 import org.broad.igv.track.AttributeManager;
 import org.broad.igv.track.Track;
-import org.broad.igv.track.TrackManager;
-import org.broad.igv.ui.*;
-import org.broad.igv.util.ResourceLocator;
+import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.RegionOfInterest;
+import org.broad.igv.ui.TrackFilter;
+import org.broad.igv.ui.TrackFilterElement;
+import org.broad.igv.ui.panel.TrackPanel;
+import org.broad.igv.ui.panel.TrackPanelScrollPane;
 import org.broad.igv.util.FileUtils;
+import org.broad.igv.util.ResourceLocator;
 import org.w3c.dom.DOMException;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
@@ -53,6 +58,8 @@ public class SessionWriter {
 
     static Logger log = Logger.getLogger(SessionWriter.class);
 
+    Session session;
+
     /**
      * Method description
      *
@@ -67,6 +74,8 @@ public class SessionWriter {
             log.error("Session Management Error", e);
         }
 
+        this.session = session;
+
         if (outputFile == null) {
             RuntimeException e = new RuntimeException("Can't save session file: " + outputFile);
             log.error("Session Management Error", e);
@@ -101,18 +110,16 @@ public class SessionWriter {
         try {
 
             // Create a DOM document
-            DocumentBuilder documentBuilder =
-                    DocumentBuilderFactory.newInstance().newDocumentBuilder();
+            DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
             Document document = documentBuilder.newDocument();
             document.setStrictErrorChecking(true);
 
             // Global root element
-            Element globalElement = document.createElement(SessionElement.GLOBAL.getText());
+            Element globalElement = document.createElement(SessionElement.SESSION.getText());
 
-            globalElement.setAttribute(SessionAttribute.VERSION.getText(),
-                    session.getSessionVersion());
+            globalElement.setAttribute(SessionAttribute.VERSION.getText(), session.getSessionVersion());
 
-            String genome = IGVModel.getInstance().getViewContext().getGenomeId();
+            String genome = session.getViewContext().getGenomeId();
             if (genome != null) {
                 globalElement.setAttribute(SessionAttribute.GENOME.getText(), genome);
             }
@@ -121,7 +128,7 @@ public class SessionWriter {
                 globalElement.setAttribute(SessionAttribute.LOCUS.getText(), locus);
             }
 
-            String groupBy = TrackManager.getInstance().getGroupByAttribute();
+            String groupBy = IGVMainFrame.getInstance().getTrackManager().getGroupByAttribute();
             if (groupBy != null) {
                 globalElement.setAttribute(SessionAttribute.GROUP_TRACKS_BY.getText(), groupBy);
             }
@@ -131,7 +138,7 @@ public class SessionWriter {
             writeResources(outputFile, globalElement, document);
 
             // Panels
-            writePanels(session, globalElement, document);
+            writePanels(globalElement, document);
 
 
             // Regions of Interest
@@ -140,10 +147,6 @@ public class SessionWriter {
             // Filter
             writeFilters(session, globalElement, document);
 
-            // Visible Attributes
-            writeVisibleAttributes(document, globalElement);
-
-
             document.appendChild(globalElement);
 
             // Transform document into XML
@@ -167,24 +170,6 @@ public class SessionWriter {
         return xmlString;
     }
 
-    private void writeVisibleAttributes(Document document, Element globalElement) {
-        Collection<String> attributes = IGVMainFrame.getInstance().getUnselectedAttributes();
-        if ((attributes != null) && !attributes.isEmpty()) {
-
-            Element hiddenAttributesElement =
-                    document.createElement(SessionElement.HIDDEN_ATTRIBUTES.getText());
-
-            for (String hiddenAttribute : attributes) {
-                Element visibleAttributeElement =
-                        document.createElement(SessionElement.HIDDEN_ATTRIBUTE.getText());
-                visibleAttributeElement.setAttribute(SessionAttribute.NAME.getText(),
-                        hiddenAttribute);
-                hiddenAttributesElement.appendChild(visibleAttributeElement);
-            }
-            globalElement.appendChild(hiddenAttributesElement);
-        }
-    }
-
     private void writeFilters(Session session, Element globalElement, Document document) {
         TrackFilter trackFilter = session.getFilter();
         if (trackFilter != null) {
@@ -232,20 +217,18 @@ public class SessionWriter {
     }
 
     private void writeRegionsOfInterest(Element globalElement, Document document) {
-        Collection<RegionOfInterest> regions = getRegionsOfInterestSet();
+        Collection<RegionOfInterest> regions = session.getAllRegionsOfInterest();
         if ((regions != null) && !regions.isEmpty()) {
 
             Element regionsElement = document.createElement(SessionElement.REGIONS.getText());
             for (RegionOfInterest region : regions) {
                 Element regionElement = document.createElement(SessionElement.REGION.getText());
-                regionElement.setAttribute(SessionAttribute.CHROMOSOME.getText(),
-                        region.getChromosomeName());
-                regionElement.setAttribute(SessionAttribute.START_INDEX.getText(),
-                        region.getStart().toString());
-                regionElement.setAttribute(SessionAttribute.END_INDEX.getText(),
-                        region.getEnd().toString());
-                regionElement.setAttribute(SessionAttribute.DESCRIPTION.getText(),
-                        region.getDescription());
+                regionElement.setAttribute(SessionAttribute.CHROMOSOME.getText(), region.getChr());
+                regionElement.setAttribute(SessionAttribute.START_INDEX.getText(), region.getStart().toString());
+                regionElement.setAttribute(SessionAttribute.END_INDEX.getText(), region.getEnd().toString());
+                if (region.getDescription() != null) {
+                    regionElement.setAttribute(SessionAttribute.DESCRIPTION.getText(), region.getDescription());
+                }
                 regionsElement.appendChild(regionElement);
             }
             globalElement.appendChild(regionsElement);
@@ -253,68 +236,100 @@ public class SessionWriter {
     }
 
     private void writeResources(File outputFile, Element globalElement, Document document) {
-        Collection<ResourceLocator> resourceFiles = getResourceLocatorSet();
-        if ((resourceFiles != null) && !resourceFiles.isEmpty()) {
-
-            Element filesElement = document.createElement(SessionElement.FILES.getText());
+        Collection<ResourceLocator> resourceLocators = getResourceLocatorSet();
+        if ((resourceLocators != null) && !resourceLocators.isEmpty()) {
 
+            Element filesElement = document.createElement(SessionElement.RESOURCES.getText());
             String isRelativeDataFile = "false";
             String filepath = null;
-            for (ResourceLocator resourceFile : resourceFiles) {
-
-                Element dataFileElement =
-                        document.createElement(SessionElement.DATA_FILE.getText());
 
-                if (resourceFile.isLocal()) {
-
-                    filepath = FileUtils.getRelativePath(outputFile.getParentFile(),
-                            resourceFile.getPath());
-                    if (filepath.equals(resourceFile.getPath())) {
-                        isRelativeDataFile = "false";
-                    } else {
-                        isRelativeDataFile = "true";
+            for (ResourceLocator resourceLocator : resourceLocators) {
+                if (resourceLocator.exists() || !(resourceLocator.getPath() == null)) {
+
+                    //RESOURCE ELEMENT
+                    Element dataFileElement =
+                            document.createElement(SessionElement.RESOURCE.getText());
+
+                    //TODO Decide whether to keep this in here.. Not really necessary.
+                    if (resourceLocator.isLocal()) {
+                        filepath = FileUtils.getRelativePath(outputFile.getParentFile(),
+                                resourceLocator.getPath());
+                        if (!(filepath.equals(resourceLocator.getPath()))) {
+                            dataFileElement.setAttribute(SessionAttribute.RELATIVE_PATH.getText(),
+                                    isRelativeDataFile);
+                        }
                     }
-                    dataFileElement.setAttribute(SessionAttribute.RELATIVE_PATH.getText(),
-                            isRelativeDataFile);
-                } else {
-                    filepath = resourceFile.getPath();
-                }
 
-                String platformIndependentPath = FileUtils.getPlatformIndependentPath(filepath);
-                dataFileElement.setAttribute(SessionAttribute.NAME.getText(),
-                        platformIndependentPath);
+                    //REQUIRED ATTRIBUTES - Cannot be null
+                    dataFileElement.setAttribute(SessionAttribute.PATH.getText(), resourceLocator.getPath());
 
-                if (!resourceFile.isLocal()) {
-                    dataFileElement.setAttribute(SessionAttribute.SERVER_URL.getText(),
-                            resourceFile.getServerURL());
-                }
+                    //OPTIONAL ATTRIBUTES
 
-                filesElement.appendChild(dataFileElement);
+                    if (resourceLocator.getName() != null) {
+                        dataFileElement.setAttribute(SessionAttribute.NAME.getText(), resourceLocator.getName());
+                    }
+                    if (resourceLocator.getServerURL() != null) {
+                        dataFileElement.setAttribute(SessionAttribute.SERVER_URL.getText(), resourceLocator.getServerURL());
+                    }
+                    if (resourceLocator.getInfolink() != null) {
+                        dataFileElement.setAttribute(SessionAttribute.HYPERLINK.getText(), resourceLocator.getInfolink());
+                    }
+                    if (resourceLocator.getUrl() != null) {
+                        dataFileElement.setAttribute(SessionAttribute.FEATURE_URL.getText(), resourceLocator.getUrl());
+                    }
+                    if (resourceLocator.getDescription() != null) {
+                        dataFileElement.setAttribute(SessionAttribute.DESCRIPTION.getText(), resourceLocator.getDescription());
+                    }
+                    if (resourceLocator.getType() != null) {
+                        dataFileElement.setAttribute(SessionAttribute.TYPE.getText(), resourceLocator.getType());
+                    }
+                    if (resourceLocator.getCoverage() != null) {
+                        dataFileElement.setAttribute(SessionAttribute.COVERAGE.getText(), resourceLocator.getCoverage());
+                    }
+                    if (resourceLocator.getTrackLine() != null) {
+                        dataFileElement.setAttribute(SessionAttribute.TRACK_LINE.getText(), resourceLocator.getTrackLine());
+                    }
+                    filesElement.appendChild(dataFileElement);
+                }
             }
             globalElement.appendChild(filesElement);
         }
     }
 
-    private void writePanels(Session session, Element globalElement, Document document) throws DOMException {
+    private void writePanels(Element globalElement, Document document) throws DOMException {
+
+        for (TrackPanel trackPanel : IGVMainFrame.getInstance().getTrackPanels()) {
 
+            // TODO -- loop through panels groups, rather than skipping groups to tracks
 
-        //  Panels
-        for (String panel : TrackManager.getInstance().getPanelNames()) {
-            List<Track> tracks = TrackManager.getInstance().getTracksForPanel(panel);
+            List<Track> tracks = trackPanel.getTracks();
             if ((tracks != null) && !tracks.isEmpty()) {
 
                 Element panelElement = document.createElement(SessionElement.PANEL.getText());
-                panelElement.setAttribute("name", panel);
+                panelElement.setAttribute("name", trackPanel.getName());
+                panelElement.setAttribute("height", String.valueOf(trackPanel.getHeight()));
+                panelElement.setAttribute("width", String.valueOf(trackPanel.getWidth()));
 
                 for (Track track : tracks) {
 
-                    Element dataTrackElement = document.createElement(SessionElement.TRACK.getText());
+                    Element trackElement = document.createElement(SessionElement.TRACK.getText());
+                    trackElement.setAttribute(SessionReader.SessionAttribute.ID.getText(), track.getId());
+                    trackElement.setAttribute(SessionReader.SessionAttribute.NAME.getText(), track.getName());
+                    for (Map.Entry<String, String> attrValue : track.getPersistentState().entrySet()) {
+                        trackElement.setAttribute(attrValue.getKey(), attrValue.getValue());
+                    }
 
-                    for (Map.Entry<String, String> attrValue : track.getSessionAttributes().entrySet()) {
-                        dataTrackElement.setAttribute(attrValue.getKey(), attrValue.getValue());
+                    // TODO -- DataRange element,  create element, append as child to track
+                    DataRange dr = track.getDataRange();
+                    if (dr != null) {
+                        Element drElement = document.createElement(SessionElement.DATA_RANGE.getText());
+                        for (Map.Entry<String, String> attrValue : dr.getPersistentState().entrySet()) {
+                            drElement.setAttribute(attrValue.getKey(), attrValue.getValue());
+                        }
+                        trackElement.appendChild(drElement);
                     }
 
-                    panelElement.appendChild(dataTrackElement);
+                    panelElement.appendChild(trackElement);
                 }
                 globalElement.appendChild(panelElement);
             }
@@ -322,14 +337,14 @@ public class SessionWriter {
     }
 
     /**
-     * @return A set of the loadd data files.
+     * @return A set of the load data files.
      */
     public Collection<ResourceLocator> getResourceLocatorSet() {
 
         Collection<ResourceLocator> locators = new ArrayList();
 
         Collection<ResourceLocator> currentTrackFileLocators =
-                TrackManager.getInstance().getDataResourceLocators();
+                IGVMainFrame.getInstance().getTrackManager().getDataResourceLocators();
 
         if (currentTrackFileLocators != null) {
             for (ResourceLocator locator : currentTrackFileLocators) {
@@ -349,15 +364,5 @@ public class SessionWriter {
         return locators;
     }
 
-    public Collection<RegionOfInterest> getRegionsOfInterestSet() {
-
-        IGVModel model = IGVModel.getInstance();
-        Collection<RegionOfInterest> regions =
-                model.getRegionsOfInterest(model.getViewContext().getChrName());
-
-        return regions;
-    }
-
-
 }
 
diff --git a/src/org/broad/igv/session/ViewContext.java b/src/org/broad/igv/session/ViewContext.java
new file mode 100644
index 0000000..771ea89
--- /dev/null
+++ b/src/org/broad/igv/session/ViewContext.java
@@ -0,0 +1,1030 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.session;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import org.apache.log4j.Logger;
+import org.broad.igv.Globals;
+import org.broad.igv.PreferenceManager;
+import org.broad.igv.feature.Chromosome;
+import org.broad.igv.feature.Genome;
+import org.broad.igv.feature.GenomeDescriptor;
+import org.broad.igv.feature.GenomeManager;
+import org.broad.igv.sam.AlignmentTrack;
+import org.broad.igv.track.Track;
+import org.broad.igv.track.TrackGroup;
+import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.UIConstants;
+import org.broad.igv.ui.panel.DataPanel;
+import org.broad.igv.ui.panel.DragEventManager;
+import org.broad.igv.ui.panel.TrackPanelScrollPane;
+import org.broad.igv.ui.util.MessageUtils;
+
+import javax.swing.*;
+import javax.swing.text.NumberFormatter;
+import java.awt.*;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.io.IOException;
+import java.text.NumberFormat;
+import java.util.*;
+import java.util.List;
+
+/**
+ * @author jrobinso
+ */
+public class ViewContext {
+
+    private static Logger log = Logger.getLogger(ViewContext.class);
+
+    private static ViewContext instance = new ViewContext();
+
+    private static NumberFormat numberFormat = NumberFormat.getInstance(Locale.US);
+
+    private String genomeId;
+
+    Genome genome;
+
+    /**
+     * The nominal viewport width in pixels.
+     */
+    public int binsPerTile = 700;
+
+    int dataPanelWidth = 700;
+
+    /**
+     * The chromosome currently in view
+     */
+    private String chrName = "chrAll";
+
+    /**
+     * The minimum zoom level for the current screen size + chromosome combination.
+     */
+    private int minZoom = 0;
+
+    /**
+     * The current zoom level.  Zoom level -1 corresponds to the whole
+     * genome view (chromosome "all")
+     */
+    private int zoom = minZoom;
+    /**
+     * The maximum zoom level.  Set to prevent integer overflow.  This is a function
+     * of chromosom length.
+     */
+    public static int maxZoom = 23;
+    /**
+     * The number of tiles for this zoom level, = 2^zoom
+     */
+    private int nTiles = 1;
+    /**
+     * The maximum virtual pixel value.
+     */
+    private double maxPixel;
+    /**
+     * The origin in bp
+     */
+    private double origin = 0;
+
+    /**
+     * The location (x axis) locationScale in base pairs / virtual pixel
+     */
+    private double locationScale;
+
+    private boolean locationScaleValid = false;
+    /**
+     * Indicates if the data is stretched to fill the window.  This happens at low
+     * resolution views when drawing at the resolution of the zoom level would
+     * only partially fill the screen.
+     */
+    private boolean stretched = false;
+
+    public History history;
+
+
+    public static ViewContext getInstance() {
+        return instance;
+    }
+
+    /**
+     * Constructs ...
+     */
+    private ViewContext() {
+        String lastChromosomeViewed = PreferenceManager.getInstance().getLastChromosomeViewed();
+
+        if ((lastChromosomeViewed == null) || lastChromosomeViewed.trim().equals("")) {
+            this.chrName = getHomeChr();
+        } else {
+            this.chrName = lastChromosomeViewed;
+
+        }
+
+        history = new History(100);
+    }
+
+    /**
+     * Return the "home chromosome" for the current genome.  This should probably be a method
+     * on the Genome class.
+     *
+     * @return
+     */
+    public String getHomeChr() {
+        return genome == null ? Globals.CHR_ALL : genome.getHomeChromosome();
+    }
+
+    /**
+     * Compute the maximum zoom level, which is a function of chromosome length.
+     */
+    private void computeMaxZoom() {
+
+        // Compute max zoom.  Assume window size @ max zoom of ~ 50 bp
+        if (genome != null && chrName != null && genome.getChromosome(chrName) != null) {
+            if (chrName.equals(Globals.CHR_ALL)) {
+                maxZoom = 0;
+            } else {
+                int chrLength = genome.getChromosome(chrName).getLength();
+
+                maxZoom = (int) (Math.log(chrLength / 50.0) / log2) + 1;
+            }
+
+            if (zoom > maxZoom) {
+                setZoom(maxZoom);
+            }
+        }
+
+
+        // TODO -- fire chromosome changed event rather than this
+    }
+
+    private void setZoom(int newZoom) {
+        zoom = Math.min(maxZoom, newZoom);
+        nTiles = (int) Math.pow(2, Math.max(minZoom, zoom));
+        maxPixel = getTilesTimesBinsPerTile();
+        invalidateLocationScale();
+
+        // TODO -- do this with events,
+        if (IGVMainFrame.hasInstance()) {
+            IGVMainFrame.getInstance().repaintStatusAndZoomSlider();
+        }
+
+    }
+
+    /**
+     * Method description
+     *
+     * @param increment
+     */
+    public void incrementZoom(int increment) {
+        zoomAndCenter((int) zoom + increment);
+    }
+
+    /**
+     * Method description
+     *
+     * @param newZoom
+     */
+    public void zoomAndCenterAdjusted(int newZoom) {
+        zoomAndCenter(minZoom + newZoom);
+    }
+
+    /**
+     * Method description
+     *
+     * @param newZoom
+     */
+    public void zoomAndCenter(int newZoom) {
+
+        // Zoom but remain centered about current center
+        double currentCenter = origin + ((dataPanelWidth / 2) * getScale());
+
+        zoomTo(newZoom, currentCenter);
+    }
+
+    /**
+     * Method description
+     *
+     * @param newZoom
+     * @param newCenter
+     */
+    public void zoomTo(final int newZoom, final double newCenter) {
+
+        if (chrName.equals(Globals.CHR_ALL)) {
+            chrName = getHomeChr();
+        }
+
+        if (chrName.equals(Globals.CHR_ALL)) {
+
+            // DISABLE ZOOMING FOR GENOME VIEW
+            // Translate the location to chromosome number
+            jumpToChromosomeForGenomeLocation(newCenter);
+            IGVMainFrame.getInstance().chromosomeChangeEvent();
+        } else {
+            if (zoom != newZoom) {
+
+                setZoom(newZoom);
+                computeLocationScaleImmediately();
+
+                double newLocationScale = getScale();
+
+                // Adjust origin so newCenter is centered
+                double newOrigin = Math.round(
+                        newCenter - ((dataPanelWidth / 2) * newLocationScale));
+
+                setOrigin(newOrigin);
+
+            }
+        }
+
+        recordHistory();
+
+        // This is a hack,  this is not a drag event but is a "jump"
+        DragEventManager.getInstance().dragStopped();
+
+    }
+
+    public void recordHistory() {
+        history.push(getFormattedLocusString());
+    }
+
+    private void jumpToChromosomeForGenomeLocation(double locationMB) {
+        double startMB = 0;
+
+        for (String chr : getGenome().getChromosomeNames()) {
+            double endMB = startMB + getGenome().getChromosome(chr).getLength() / 1000.0;
+
+            if ((locationMB > startMB) && (locationMB <= endMB)) {
+
+                // this.jumpTo(chr, -1, -1);
+                this.setChromosomeName(chr);
+                break;
+            }
+
+            startMB = endMB;
+        }
+    }
+
+    /**
+     * Method description
+     *
+     * @param delta
+     */
+    public void shiftOriginPixels(double delta) {
+        double shiftBP = delta * getScale();
+        setOrigin(origin + shiftBP);
+    }
+
+    public void snapToGrid() {
+        setOrigin(Math.round(origin));
+    }
+
+    /**
+     * Method description
+     *
+     * @param chrLocation
+     */
+    public void centerOnLocation(String chr, double chrLocation) {
+        if (!chrName.equals(chr)) {
+            chrName = chr;
+            computeMaxZoom();
+            if (zoom > maxZoom) {
+                setZoom(maxZoom);
+            }
+        }
+        double windowWidth = (dataPanelWidth * getScale()) / 2;
+        setOrigin(Math.round(chrLocation - windowWidth));
+    }
+
+    /**
+     * Method description
+     *
+     * @param chrLocation
+     */
+    public void centerOnLocation(double chrLocation) {
+        double windowWidth = (dataPanelWidth * getScale()) / 2;
+        setOrigin(Math.round(chrLocation - windowWidth));
+        recordHistory();
+    }
+
+    public boolean windowAtEnd() {
+        double windowLengthBP = dataPanelWidth * getScale();
+        return origin + windowLengthBP + 1 > getChromosomeLength();
+
+    }
+
+
+    /* Keep origin within data range */
+
+    /**
+     * Method description
+     *
+     * @param newOrigin
+     */
+    public void setOrigin(double newOrigin) {
+
+        int windowLengthBP = (int) (dataPanelWidth * getScale());
+        if (PreferenceManager.getInstance().getSAMPreferences().isShowSoftClipped()) {
+            origin = Math.max(-1000, Math.min(newOrigin, getChromosomeLength() + 1000 - windowLengthBP));
+        } else {
+            origin = Math.max(0, Math.min(newOrigin, getChromosomeLength() - windowLengthBP));
+        }
+
+        for (TrackPanelScrollPane sp : IGVMainFrame.getInstance().getTrackManager().getTrackPanelScrollPanes()) {
+            preloadTrackData(sp.getDataPanel());
+        }
+
+        // If zoomed in sufficiently track the center position
+        if (locationScale < 10) {
+            IGVMainFrame.getInstance().setStatusBarMessage(chrName + ":" + ((int) getCenter() + 1));
+        }
+
+        // Repaint
+        IGVMainFrame.getInstance().repaintDataAndHeaderPanels();
+        IGVMainFrame.getInstance().repaintStatusAndZoomSlider();
+    }
+
+    static double log2 = Math.log(2);
+
+    public void jumpTo(String chr, int start, int end) {
+
+        if (chr != null) {
+            if (genome.getChromosome(chr) == null && !chr.contains(Globals.CHR_ALL)) {
+                JOptionPane.showMessageDialog(IGVMainFrame.getInstance(),
+                        chr + " is not a valid chromosome.");
+                return;
+            }
+            setChromosomeName(chr);
+        }
+
+        if (start >= 0) {
+
+            int z = (int) (Math.log(getChromosomeLength() / (end - start)) / log2) + 1;
+            if (z != this.zoom) {
+                zoom = Math.min(maxZoom, Math.max(minZoom, z));
+                nTiles = (int) Math.pow(2, zoom);
+                maxPixel = getTilesTimesBinsPerTile();
+            }
+
+            int w = dataPanelWidth;
+            setLocationScale(((double) (end - start)) / w);
+            origin = start;
+        }
+
+
+        for (TrackPanelScrollPane sp : IGVMainFrame.getInstance().getTrackManager().getTrackPanelScrollPanes()) {
+            preloadTrackData(sp.getDataPanel());
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("Data panel width = " + dataPanelWidth);
+            log.debug("New start = " + (int) getOrigin());
+            log.debug("New end = " + (int) getEnd());
+            log.debug("New center = " + (int) getCenter());
+        }
+
+
+        // Repaint
+        IGVMainFrame.getInstance().repaintDataAndHeaderPanels();
+        IGVMainFrame.getInstance().repaintStatusAndZoomSlider();
+
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public double getOrigin() {
+        return origin;
+    }
+
+    public double getCenter() {
+        return origin + getScale() * dataPanelWidth / 2;
+    }
+
+    public double getEnd() {
+        return origin + getScale() * dataPanelWidth;
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public int getZoom() {
+        return zoom;
+    }
+
+    /**
+     * Return the maximum zoom level
+     *
+     * @return
+     */
+    public int getMaxZoom() {
+        return maxZoom;
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public int getAdjustedZoom() {
+        return zoom - minZoom;
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public int getNTiles() {
+        return nTiles;
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public double getMaxPixel() {
+        return maxPixel;
+    }
+
+    /**
+     * Method description
+     *
+     * @param panel
+     */
+    public void setDataPanel(DataPanel panel) {
+
+        if (panel != null) {
+
+            panel.addComponentListener(new ComponentAdapter() {
+
+                @Override
+                public void componentResized(ComponentEvent e) {
+                    int w = e.getComponent().getWidth();
+                    if (w != dataPanelWidth) {
+                        dataPanelWidth = w;
+                        invalidateLocationScale();
+                    }
+                }
+            });
+        }
+    }
+
+    /**
+     * Method description
+     *
+     * @param name
+     */
+    public void setChrName(String name) {
+        this.setChromosomeName(name);
+    }
+
+    /**
+     * @param name
+     * @param force
+     * @ deprecated, replace with calls to setChrName();
+     */
+    public void setChromosomeName(String name, boolean force) {
+
+        if ((chrName == null) || !name.equals(chrName) || force) {
+            chrName = name;
+            origin = 0;
+            setZoom(0);
+            computeMaxZoom();
+        }
+    }
+
+    /**
+     * @param name
+     * @ deprecated, replace with calls to setChrName();
+     */
+    public void setChromosomeName(String name) {
+        setChromosomeName(name, false);
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public String getChrName() {
+        return chrName;
+    }
+
+    public String getNextChrName(String chr) {
+        List<String> chrList = genome.getChromosomeNames();
+        for (int i = 0; i < chrList.size() - 1; i++) {
+            if (chrList.get(i).equals(chr)) {
+                return chrList.get(i + 1);
+            }
+        }
+        return null;
+    }
+
+    public String getPrevChrName(String chr) {
+        List<String> chrList = genome.getChromosomeNames();
+        for (int i = chrList.size() - 1; i > 0; i--) {
+            if (chrList.get(i).equals(chr)) {
+                return chrList.get(i - 1);
+            }
+        }
+        return null;
+    }
+
+    // TODO -- this parameter shouldn't be stored here.  Maybe in a specialized
+    // layout manager?
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public int getDataPanelWidth() {
+        return dataPanelWidth;
+    }
+
+    /**
+     * Return the current locationScale in base pairs / pixel
+     *
+     * @return
+     */
+    public double getScale() {
+        if ((locationScale == 0) || !locationScaleValid) {
+            computeLocationScaleImmediately();
+        }
+
+        if (locationScale < 0) {
+            System.err.println("Negative scale");
+        }
+
+        return locationScale;
+    }
+
+    /**
+     * Method description
+     */
+    public void invalidateLocationScale() {
+        //Thread.dumpStack();
+        locationScaleValid = false;
+    }
+
+    public void computeLocationScaleImmediately() {
+
+        if (genome != null) {
+
+            computeMinZoom();
+
+            double virtualPixelSize = getTilesTimesBinsPerTile();
+
+            stretched = virtualPixelSize < dataPanelWidth;
+
+            double nPixel = Math.max(virtualPixelSize, dataPanelWidth);
+
+            setLocationScale(((double) getChromosomeLength()) / nPixel);
+        }
+    }
+
+    /**
+     * Compute the minimum zoom level for the data panel width.  This is defined as the maximum
+     * zoom level for which all data bins will fit in the window without loss of
+     * data,  i.e. the maximum zoom level for which nBins < nPixels.  The number
+     * of bins is defined as
+     * nBins =  2^z
+     * so minZoom is the value z such that nBins < dataPanelWidth
+     */
+    private void computeMinZoom() {
+        if (this.chrName.equals(Globals.CHR_ALL)) {
+            minZoom = 0;
+        } else {
+            minZoom = Math.max(0, (int) (Math.log((dataPanelWidth / binsPerTile)) / log2));
+
+            if (zoom < minZoom) {
+                zoom = minZoom;
+                nTiles = (int) Math.pow(2, zoom);
+                maxPixel = getTilesTimesBinsPerTile();
+            }
+        }
+
+    }
+
+    /**
+     * Return the chromosome position corresponding to the pixel index.  The
+     * pixel index is the pixel "position" translated by -origin.
+     *
+     * @param pixelIndex
+     * @return
+     */
+    public double getChromosomePosition(int pixelIndex) {
+        return origin + (getScale() * pixelIndex);
+    }
+
+    /**
+     * Return the pixel position corresponding to the chromosomal position.
+     *
+     * @param chromosomePosition
+     * @return
+     */
+    public int getPixelPosition(double chromosomePosition) {
+        return (int) ((chromosomePosition - origin) / getScale());
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public Chromosome getChromosome() {
+        if (genome == null) {
+            log.error("Genome not loaded!");
+            return null;
+        }
+
+        return genome.getChromosome(chrName);
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public int getChromosomeLength() {
+
+        if (genome == null) {
+            return 1;
+        }
+
+        if (chrName.equals("All")) {
+
+            // TODO -- remove the hardcoded unit divider ("1000")
+            return (int) (genome.getLength() / 1000);
+
+            // return genome.getLength();
+        } else {
+            if (getChromosome() == null) {
+                System.out.println("Null chromosome: " + chrName);
+                if (genome == null) {
+                    return 1;
+                } else {
+                    return genome.getChromosomes().iterator().next().getLength();
+                }
+            }
+
+            return getChromosome().getLength();
+        }
+    }
+
+    // /////////////////////////////////////////////////////////////////////////
+    // Genome methods /////////////////////////////////////////////////////////
+
+    /**
+     * Attempt to switch genomes to newGenome.  Return the actual genome, which
+     * might differ if the load of newGenome fails.
+     *
+     * @param newGenome
+     */
+    public String setGenomeId(String newGenome) {
+
+        if (genomeId != null && !genomeId.equals(newGenome)) {
+            history.clear();
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("Setting genome id: " + newGenome);
+        }
+
+        boolean startUp = (genomeId == null);
+        boolean loadFailed = false;
+
+        genomeId = newGenome;
+        if (!GenomeManager.getInstance().isGenomeLoaded(genomeId)) {
+            try {
+                if (log.isDebugEnabled()) {
+                    log.debug("findGenomeAndLoad: " + genomeId);
+                }
+                GenomeManager.getInstance().findGenomeAndLoad(genomeId);
+            } catch (IOException e) {
+                log.error("Error loading genome: " + genomeId, e);
+                loadFailed = true;
+                MessageUtils.showMessage("Load of genome: " + genomeId + " failed.");
+            }
+        }
+        genome = GenomeManager.getInstance().getGenome(genomeId);
+
+        if (genome == null || loadFailed) {
+            GenomeManager.GenomeListItem defaultDesc = GenomeManager.getInstance().getTopGenomeListItem();
+            String msg = "Could not locate genome: " + genomeId + ".  Loading " + defaultDesc.getDisplayableName();
+            MessageUtils.showMessage(msg);
+            log.error("Could not locate genome: " + genomeId + ".  Loading " + defaultDesc.getDisplayableName());
+
+            // The previously used genome is unavailable.   Load hg18, we are assuming that
+            // this is always available.  // TODO -- permit IGV starting with no selected genome
+            genomeId = defaultDesc.getId();
+            try {
+                if (log.isDebugEnabled()) {
+                    log.debug("findGenomeAndLoad: " + genomeId);
+                }
+                GenomeManager.getInstance().findGenomeAndLoad(genomeId);
+            } catch (IOException e) {
+                log.error("Error loading genome: " + genomeId, e);
+                MessageUtils.showMessage("<html>Load of genome: " + genomeId + " failed." +
+                        "<br>IGV is in an ustable state and will be closed." +
+                        "<br>Please report this error to igv-help at broadinstitute.org");
+                System.exit(-1);
+            }
+
+            if (log.isDebugEnabled()) {
+                log.debug("Get genome id");
+            }
+            genome = GenomeManager.getInstance().getGenome(genomeId);
+
+            PreferenceManager.getInstance().setDefaultGenome(genomeId);
+            this.setChrName(getHomeChr());
+
+
+        } else if (!startUp) {
+            // We don't know what chromosomes the new genome has, set to "all" (whole genome)
+            this.setChrName(getHomeChr());
+        }
+        computeMaxZoom();
+        invalidateLocationScale();
+        if (!Globals.isHeadless()) {
+            IGVMainFrame.getInstance().chromosomeChangeEvent();
+        }
+        return genomeId;
+    }
+
+    /**
+     * Version for command line tools.
+     *
+     * @param newGenome
+     */
+    public void setGenomeIdHeadless(String newGenome) {
+
+
+        genomeId = newGenome;
+        genome = GenomeManager.getInstance().getGenome(genomeId);
+
+
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public String getGenomeId() {
+        return genomeId;
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public Genome getGenome() {
+        return genome;
+    }
+
+    /**
+     * Return the collection of chromosome names for the currently loaded
+     * genome
+     *
+     * @return
+     */
+    public Collection<String> getChromosomeNames() {
+        return genome.getChromosomeNames();
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public boolean isStretched() {
+        return stretched;
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public double getTilesTimesBinsPerTile() {
+        return (double) nTiles * (double) binsPerTile;
+    }
+
+    /**
+     * Get the UCSC style locus string corresponding to the current view.  THe UCSC
+     * conventions are followed for coordinates,  specifically the internal representation
+     * is "zero" based (first base is numbered 0) but the display representation is
+     * "one" based (first base is numbered 1).   Consequently 1 is added to the
+     * computed positions.
+     *
+     * @return
+     */
+    public String getFormattedLocusString() {
+
+        if (zoom == 0) {
+            return getChrName();
+        } else {
+            Range range = getCurrentRange();
+            String startStr = numberFormat.format(range.getStart());
+            String endStr = numberFormat.format(range.getEnd());
+            String position = range.getChr() + ":" + startStr + "-" + endStr;
+            return position;
+        }
+
+    }
+
+    public Range getCurrentRange() {
+        int start = 0;
+        int end = getDataPanelWidth();
+        int startLoc = (int) getChromosomePosition(start) + 1;
+        int endLoc = (int) getChromosomePosition(end);
+        Range range = new Range(getChrName(), startLoc, endLoc);
+        return range;
+    }
+
+    private void setLocationScale(double locationScale) {
+        this.locationScale = locationScale;
+        locationScaleValid = true;
+
+        //if (log.isDebugEnabled()) {
+        //    log.debug("locationScale = " + locationScale);
+        //}
+    }
+
+    /**
+     *
+     */
+    private void preloadTrackData(DataPanel dp) {
+
+        Rectangle visibleRect = dp.getVisibleRect();
+        int start = (int) origin;
+        int end = (int) (start + dp.getWidth() * locationScale);
+        int trackY = 0;
+        Collection<TrackGroup> groups = dp.getTrackGroups();
+
+        for (Iterator<TrackGroup> groupIter = groups.iterator(); groupIter.hasNext();) {
+            TrackGroup group = groupIter.next();
+            if (group.isVisible()) {
+                if (groups.size() > 1) {
+                    trackY += UIConstants.groupGap;
+                }
+                for (Track track : group.getTracks()) {
+                    int trackHeight = track.getHeight();
+
+                    if (!(track instanceof AlignmentTrack) && visibleRect != null) {
+                        if (trackY > visibleRect.y + visibleRect.height) {
+                            break;
+                        } else if (trackY + trackHeight < visibleRect.y) {
+                            trackY += trackHeight;
+                            continue;
+                        }
+                    }
+
+
+                    if (track.isVisible()) {
+                        //    track.preloadData(chrName, start, end, zoom);
+                        //
+                        trackY += track.getHeight();
+                    }
+                }
+            }
+
+        }
+    }
+
+    public List<String> getAllHistory() {
+        return history.getAllHistory();
+    }
+
+    public static class Range {
+        private String chr;
+        private int start;
+        private int end;
+
+        public Range(String chr, int start, int end) {
+            this.chr = chr;
+            this.start = start;
+            this.end = end;
+        }
+
+        public String getChr() {
+            return chr;
+        }
+
+        public int getStart() {
+            return start;
+        }
+
+        public int getEnd() {
+            return end;
+        }
+    }
+
+    public static class History {
+
+        int maxEntries = 100;
+        int currPos = 0;
+
+        LinkedList<String> activeStack;
+        List<String> allHistory;
+
+        History(int maxEntries) {
+            this.maxEntries = maxEntries;
+            activeStack = new LinkedList();
+            allHistory = new ArrayList();
+        }
+
+
+        public void push(String s) {
+
+            log.debug("History: " + s);
+            allHistory.add(s);
+
+            while (currPos > 0) {
+                activeStack.removeFirst();
+                currPos--;
+            }
+            activeStack.addFirst(s);
+
+
+        }
+
+
+        public String back() {
+            if (activeStack.size() == 0 || currPos >= (activeStack.size() - 1)) {
+                return null;
+            }
+            currPos++;
+            return activeStack.get(currPos);
+        }
+
+        public String forward() {
+
+            if (activeStack.size() == 0 || currPos == 0) {
+                return null;
+            }
+            currPos--;
+            printStack();
+            return activeStack.get(currPos);
+        }
+
+
+        public String peekBack() {
+            if (activeStack.size() == 0 || (currPos + 1) >= activeStack.size()) {
+                return null;
+            }
+            return activeStack.get(currPos + 1);
+        }
+
+        public String peekForward() {
+            if (activeStack.size() == 0 || (currPos - 1) < 0) {
+                return null;
+            }
+            return activeStack.get(currPos - 1);
+        }
+
+        public void clear() {
+            activeStack.clear();
+            allHistory.clear();
+            currPos = 0;
+        }
+
+        public void printStack() {
+            System.out.println("curr pos=" + currPos);
+            for (String s : activeStack) {
+                System.out.println(s);
+            }
+            System.out.println();
+        }
+
+        public List<String> getAllHistory() {
+            return allHistory;
+        }
+
+    }
+}
diff --git a/src/org/broad/igv/svg/TestSVGGen.java b/src/org/broad/igv/svg/TestSVGGen.java
new file mode 100644
index 0000000..37818a4
--- /dev/null
+++ b/src/org/broad/igv/svg/TestSVGGen.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.svg;
+
+import java.awt.Rectangle;
+import java.awt.Graphics2D;
+import java.awt.Color;
+import java.io.Writer;
+import java.io.OutputStreamWriter;
+import java.io.IOException;
+
+import org.apache.batik.svggen.SVGGraphics2D;
+import org.apache.batik.dom.GenericDOMImplementation;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.DOMImplementation;
+
+public class TestSVGGen {
+
+    public void paint(Graphics2D g2d) {
+        g2d.setPaint(Color.red);
+        g2d.fill(new Rectangle(10, 10, 100, 100));
+    }
+
+    public static void main(String[] args) throws IOException {
+
+        // Get a DOMImplementation.
+        DOMImplementation domImpl =
+            GenericDOMImplementation.getDOMImplementation();
+
+        // Create an instance of org.w3c.dom.Document.
+        String svgNS = "http://www.w3.org/2000/svg";
+        Document document = domImpl.createDocument(svgNS, "svg", null);
+
+        // Create an instance of the SVG Generator.
+        SVGGraphics2D svgGenerator = new SVGGraphics2D(document);
+
+        // Ask the test to render into the SVG Graphics2D implementation.
+        TestSVGGen test = new TestSVGGen();
+        test.paint(svgGenerator);
+
+        // Finally, stream out SVG to the standard output using
+        // UTF-8 encoding.
+        boolean useCSS = true; // we want to use CSS style attributes
+        Writer out = new OutputStreamWriter(System.out, "UTF-8");
+        svgGenerator.stream(out, useCSS);
+    }
+}
\ No newline at end of file
diff --git a/src/org/broad/igv/synteny/BlastMapping.java b/src/org/broad/igv/synteny/BlastMapping.java
new file mode 100644
index 0000000..3df63e7
--- /dev/null
+++ b/src/org/broad/igv/synteny/BlastMapping.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.synteny;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import org.broad.igv.feature.BasicFeature;
+import org.broad.igv.feature.Strand;
+
+
+/**
+ * // TODO -- consider implementing feature interface
+ *
+ * @author jrobinso
+ */
+public class BlastMapping extends BasicFeature {
+
+    private Block queryBlock;
+    private Block subjectBlock;
+
+    /**
+     * Constructs ...
+     *
+     * @param queryBlock
+     * @param subjectBlock
+     * @param percentIden
+     */
+    public BlastMapping(Block queryBlock, Block subjectBlock, float percentIden) {
+        this.queryBlock = queryBlock;
+        this.subjectBlock = subjectBlock;
+
+        this.setStart(Math.min(queryBlock.getStart(), queryBlock.getEnd()));
+        this.setEnd(Math.max(queryBlock.getStart(), queryBlock.getEnd()));
+        this.setName("[" + subjectBlock.getContig() + ":" + subjectBlock.getStart() + "-" + subjectBlock.getEnd() + "]");
+        this.setScore(percentIden);
+
+    }
+
+
+    public Block getQueryBlock() {
+        return queryBlock;
+    }
+
+
+    public Block getSubjectBlock() {
+        return subjectBlock;
+    }
+
+    @Override
+    public String getChr() {
+        return queryBlock.getContig();
+    }
+
+    public Strand getStrand() {
+        return subjectBlock.getStrand();
+    }
+
+    public boolean containsQueryPosition(String contig, int position) {
+        return queryBlock.containsPosition(contig, position);
+    }
+
+
+    /**
+ * @author jrobinso
+     */
+    public static class Block {
+
+        private String contig;
+        private int start;
+        private int end;
+        Strand strand;
+
+        /**
+         * Constructs ...
+         *
+         * @param contig
+         * @param start
+         * @param end
+         */
+        public Block(String contig, int start, int end, Strand strand) {
+            this.contig = contig;
+            this.start = start;
+            this.end = end;
+            this.strand = strand;
+        }
+
+
+        public String getContig() {
+            return contig;
+        }
+
+
+        public int getStart() {
+            return start;
+        }
+
+        public int getEnd() {
+            return end;
+        }
+
+        public Strand getStrand() {
+            return strand;
+        }
+
+        public boolean containsPosition(String contig, int position) {
+            return this.contig.equals(contig) && position >= start && position < end;
+        }
+
+    }
+}
diff --git a/src/org/broad/igv/synteny/BlastMappingUtils.java b/src/org/broad/igv/synteny/BlastMappingUtils.java
new file mode 100644
index 0000000..6f8bb30
--- /dev/null
+++ b/src/org/broad/igv/synteny/BlastMappingUtils.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+
+package org.broad.igv.synteny;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import org.broad.igv.data.BasicScore;
+import org.broad.igv.data.DataUtils;
+import org.broad.igv.data.WiggleDataset;
+import org.broad.igv.data.WiggleParser;
+import org.broad.igv.feature.FeatureUtils;
+import org.broad.igv.util.ResourceLocator;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * @author jrobinso
+ */
+public class BlastMappingUtils {
+
+
+    /**
+     * Method description
+     *
+     * @param dataset
+     * @param mappings
+     * @param outputFile
+     */
+    public static void MapWigFile(WiggleDataset dataset, List<BlastMapping> mappings, File outputFile) {
+
+        PrintWriter pw = null;
+
+        try {
+            pw = new PrintWriter(new FileWriter(outputFile));
+
+            // Segregate and sort ortholog
+            Map<String, List<BlastMapping>> featureMap = new LinkedHashMap();
+            for (BlastMapping f : mappings) {
+                List<BlastMapping> fList = featureMap.get(f.getChr());
+                if (fList == null) {
+                    fList = new ArrayList();
+                    featureMap.put(f.getChr(), fList);
+                }
+                fList.add(f);
+            }
+
+            for (List<BlastMapping> featureList : featureMap.values()) {
+                FeatureUtils.sortFeatureList(featureList);
+            }
+
+            pw.println("Chr\tStart\tEnd\t\tnucCount (Kwal)");
+            // Loop through chromosomes
+            for (String chr : featureMap.keySet()) {
+                List<BlastMapping> mappingList = featureMap.get(chr);
+                List<BasicScore> scores = new ArrayList(mappingList.size());
+
+                for (BlastMapping mapping : mappingList) {
+                    BlastMapping.Block queryBlock = mapping.getQueryBlock();
+                    BlastMapping.Block subjectBlock = mapping.getSubjectBlock();
+
+                    int[] subjectPosition = dataset.getStartLocations(subjectBlock.getContig());
+                    float[] data = dataset.getData("ignore", subjectBlock.getContig());
+
+                    int s0 = subjectBlock.getStart();
+                    int s1 = subjectBlock.getEnd();
+                    int q0 = queryBlock.getStart();
+                    int q1 = queryBlock.getEnd();
+
+                    int idx0 = DataUtils.getIndexBefore(subjectPosition, Math.min(s0, s1));
+                    int idx1 = DataUtils.getIndexBefore(subjectPosition, Math.max(s0, s1)) + 1;
+                    if (idx1 == subjectPosition.length) {
+                        idx1--;
+                    }
+
+                    double beta = ((double) (q1 - q0)) / (s1 - s0);
+
+                    for (int i = idx0; i <= idx1; i++) {
+                        int pos = (int) (q0 + beta * (subjectPosition[i] - s0));
+                        float d = data[i];
+                        scores.add(new BasicScore(chr, pos, pos + 1, d));
+                    }
+                }
+
+                FeatureUtils.sortFeatureList(scores);
+                for (BasicScore s : scores) {
+                    pw.println(s.getChromosome() + "\t" + s.getStart() + "\t" +
+                            (s.getEnd() + 1) + "\t\t" + s.getScore());
+
+                }
+            }
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+        finally {
+            pw.close();
+        }
+    }
+
+
+    /**
+     * Method description
+     *
+     * @param args
+     */
+    public static void main(String[] args) {
+
+        String file = "/Users/jrobinso/Alex/Cgla_v_Kwal1.orthologs";
+        String wiggleFile = "/Users/jrobinso/IGVTestData/alex/nucCount_Kwal.wig";
+        File outputFile = new File("/Users/jrobinso/Alex/uncount_Kwal_to_Cgla.igv");
+
+        List<BlastMapping> mappings = (new BlastParser()).parse(file);
+
+        WiggleDataset ds = (new WiggleParser(new ResourceLocator(wiggleFile), "Kwal")).parse();
+
+        MapWigFile(ds, mappings, outputFile);
+
+    }
+
+
+}
diff --git a/src/org/broad/igv/synteny/BlastParser.java b/src/org/broad/igv/synteny/BlastParser.java
new file mode 100644
index 0000000..6a4a572
--- /dev/null
+++ b/src/org/broad/igv/synteny/BlastParser.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+package org.broad.igv.synteny;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import org.broad.igv.exceptions.ParserException;
+import org.broad.igv.feature.Strand;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author jrobinso
+ */
+public class BlastParser {
+
+    /**
+     * Method description
+     * <p/>
+     * queryID,subjectID,percentIden,alignmentLength,
+     * mismatches,gapOpenings,queryStart,queryStop,subjectStart,subjectStop,eval,bitScore
+     * <p/>
+     * 1	9	87.73	864	106	0	100842	101705	222166	221303	0	872
+     *
+     * @param file
+     * @return
+     */
+    public List<BlastMapping> parse(String file) {
+
+        List<BlastMapping> mappings = new ArrayList();
+
+        BufferedReader reader = null;
+
+        boolean tabblastn = file.contains(".tabblastn");
+
+        int queryChrIdx = 0;
+        int queryStartIdx = tabblastn ? 6 : 1;
+        int queryEndIdx = tabblastn ? 7 : 2;
+        int queryStrandIdx = tabblastn ? -1 : 3;
+
+        int subjectChrIdx = tabblastn ? 1 : 4;
+        int subjectStartIdx = tabblastn ? 8 : 5;
+        int subjectEndIdx = tabblastn ? 9 : 6;
+        int subjectStrandIdx = tabblastn ? -1 : 7;
+
+        int nTokens = tabblastn ? 10 : 8;
+
+        long lineCount = 0;
+        String nextLine = null;
+        boolean dataParsed = false;
+
+        try {
+            reader = new BufferedReader(new FileReader(file));
+
+            while ((nextLine = reader.readLine()) != null) {
+                lineCount++;
+                String[] tokens = nextLine.split("\t");
+                if (tokens.length >= nTokens) {
+                    dataParsed = true;
+                    String queryCht = tokens[queryChrIdx];
+
+                    int queryStart;
+                    int queryEnd;
+                    try {
+                        queryStart = Integer.parseInt(tokens[queryStartIdx]);
+                        queryEnd = Integer.parseInt(tokens[queryEndIdx]);
+                    }
+                    catch (NumberFormatException ne) {
+                        throw new RuntimeException("Non-numeric value found in either start or end column");
+                    }
+                    Strand queryStrand = Strand.NONE;
+                    if (!tabblastn) {
+                        int str = Integer.parseInt(tokens[queryStrandIdx]);
+                        queryStrand = str == 1 ? Strand.POSITIVE : Strand.NEGATIVE;
+                    }
+
+                    String subjectChr = tokens[subjectChrIdx];
+
+                    int subjectStart;
+                    int subjectEnd;
+                    try {
+                        subjectStart = Integer.parseInt(tokens[subjectStartIdx]);
+                        subjectEnd = Integer.parseInt(tokens[subjectEndIdx]);
+                    }
+                    catch (NumberFormatException ne) {
+                        throw new RuntimeException("Non-numeric value found in " +
+                                " either subject start or end column");
+                    }
+                    Strand subjectStrand = Strand.NONE;
+                    if (!tabblastn) {
+                        try {
+                            int str = Integer.parseInt(tokens[subjectStrandIdx]);
+                            subjectStrand = str == 1 ? Strand.POSITIVE : Strand.NEGATIVE;
+                        }
+                        catch (NumberFormatException ne) {
+                            throw new RuntimeException("Non-numeric value found in " +
+                                    " either subject strand column");
+                        }
+                    }
+
+                    float percentIden = tabblastn ? Float.parseFloat(tokens[2]) : 100.0f;
+
+                    BlastMapping.Block queryBlock = new BlastMapping.Block(queryCht, queryStart, queryEnd, queryStrand);
+                    BlastMapping.Block subjectBlock = new BlastMapping.Block(subjectChr, subjectStart, subjectEnd, subjectStrand);
+
+                    mappings.add(new BlastMapping(queryBlock, subjectBlock, percentIden));
+                }
+            }
+
+            if (!dataParsed) {
+                throw new RuntimeException("Data not loaded. Number of columns is less than " + nTokens + ".");
+            }
+
+        } catch (Exception ex) {
+            if (lineCount != 0) {
+                throw new ParserException(ex.getMessage(), lineCount, nextLine);
+            } else {
+                throw new RuntimeException(ex);
+            }
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+
+                } catch (IOException iOException) {
+                }
+            }
+        }
+
+        return mappings;
+    }
+}
diff --git a/src/org/broad/igv/synteny/Block.java b/src/org/broad/igv/synteny/Block.java
deleted file mode 100644
index e8a27ff..0000000
--- a/src/org/broad/igv/synteny/Block.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-
-package org.broad.igv.synteny;
-
-import org.broad.igv.feature.Strand;
-
-
-/**
- * @author jrobinso
- */
-public class Block {
-
-    private String contig;
-    private int start;
-    private int end;
-    Strand strand;
-
-    /**
-     * Constructs ...
-     *
-     * @param contig
-     * @param start
-     * @param end
-     */
-    public Block(String contig, int start, int end, Strand strand) {
-        this.contig = contig;
-        this.start = start;
-        this.end = end;
-        this.strand = strand;
-    }
-
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public String getContig() {
-        return contig;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public int getStart() {
-        return start;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public int getEnd() {
-        return end;
-    }
-
-    public Strand getStrand() {
-        return strand;
-    }
-
-}
diff --git a/src/org/broad/igv/synteny/Mapping.java b/src/org/broad/igv/synteny/Mapping.java
deleted file mode 100644
index a5e926d..0000000
--- a/src/org/broad/igv/synteny/Mapping.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-package org.broad.igv.synteny;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import org.broad.igv.feature.BasicFeature;
-import org.broad.igv.feature.Strand;
-
-
-/**
- * // TODO -- consider implementing feature interface
- *
- * @author jrobinso
- */
-public class Mapping extends BasicFeature {
-
-    private Block queryBlock;
-    private Block subjectBlock;
-
-    /**
-     * Constructs ...
-     *
-     * @param queryBlock
-     * @param subjectBlock
-     * @param percentIden
-     */
-    public Mapping(Block queryBlock, Block subjectBlock, float percentIden) {
-        this.queryBlock = queryBlock;
-        this.subjectBlock = subjectBlock;
-
-        this.setStart(Math.min(queryBlock.getStart(), queryBlock.getEnd()));
-        this.setEnd(Math.max(queryBlock.getStart(), queryBlock.getEnd()));
-        this.setName("[" + subjectBlock.getContig() + ":" + subjectBlock.getStart() + "-"
-                + subjectBlock.getEnd() + "]");
-        this.setScore(percentIden);
-
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public Block getQueryBlock() {
-        return queryBlock;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public Block getSubjectBlock() {
-        return subjectBlock;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    @Override
-    public String getChromosome() {
-        return queryBlock.getContig();
-    }
-
-    public Strand getStrand() {
-        return subjectBlock.getStrand();
-    }
-
-
-}
diff --git a/src/org/broad/igv/synteny/MappingParser.java b/src/org/broad/igv/synteny/MappingParser.java
deleted file mode 100644
index e7e9ad1..0000000
--- a/src/org/broad/igv/synteny/MappingParser.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-package org.broad.igv.synteny;
-
-//~--- JDK imports ------------------------------------------------------------
-
-import org.broad.igv.exceptions.ParserException;
-import org.broad.igv.feature.Strand;
-
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @author jrobinso
- */
-public class MappingParser {
-
-    /**
-     * Method description
-     * <p/>
-     * queryID,subjectID,percentIden,alignmentLength,
-     * mismatches,gapOpenings,queryStart,queryStop,subjectStart,subjectStop,eval,bitScore
-     * <p/>
-     * 1	9	87.73	864	106	0	100842	101705	222166	221303	0	872
-     *
-     * @param file
-     * @return
-     */
-    public List<Mapping> parse(String file) {
-
-        List<Mapping> mappings = new ArrayList();
-
-        BufferedReader reader = null;
-
-        boolean tabblastn = file.contains(".tabblastn");
-
-        int queryChrIdx = 0;
-        int queryStartIdx = tabblastn ? 6 : 1;
-        int queryEndIdx = tabblastn ? 7 : 2;
-        int queryStrandIdx = tabblastn ? -1 : 3;
-
-        int subjectChrIdx = tabblastn ? 1 : 4;
-        int subjectStartIdx = tabblastn ? 8 : 5;
-        int subjectEndIdx = tabblastn ? 9 : 6;
-        int subjectStrandIdx = tabblastn ? -1 : 7;
-
-        int nTokens = tabblastn ? 10 : 8;
-
-        long lineCount = 0;
-        String nextLine = null;
-        boolean dataParsed = false;
-
-        try {
-            reader = new BufferedReader(new FileReader(file));
-
-            while ((nextLine = reader.readLine()) != null) {
-                lineCount++;
-                String[] tokens = nextLine.split("\t");
-                if (tokens.length >= nTokens) {
-                    dataParsed = true;
-                    String queryCht = tokens[queryChrIdx];
-
-                    int queryStart;
-                    int queryEnd;
-                    try {
-                        queryStart = Integer.parseInt(tokens[queryStartIdx]);
-                        queryEnd = Integer.parseInt(tokens[queryEndIdx]);
-                    }
-                    catch (NumberFormatException ne) {
-                        throw new RuntimeException("Non-numeric value found in " +
-                                " either start or end column");
-                    }
-                    Strand queryStrand = Strand.NONE;
-                    if (!tabblastn) {
-                        int str = Integer.parseInt(tokens[queryStrandIdx]);
-                        queryStrand = str == 1 ? Strand.POSITIVE : Strand.NEGATIVE;
-                    }
-
-                    String subjectChr = tokens[subjectChrIdx];
-
-                    int subjectStart;
-                    int subjectEnd;
-                    try {
-                        subjectStart = Integer.parseInt(tokens[subjectStartIdx]);
-                        subjectEnd = Integer.parseInt(tokens[subjectEndIdx]);
-                    }
-                    catch (NumberFormatException ne) {
-                        throw new RuntimeException("Non-numeric value found in " +
-                                " either subject start or end column");
-                    }
-                    Strand subjectStrand = Strand.NONE;
-                    if (!tabblastn) {
-                        try {
-                            int str = Integer.parseInt(tokens[subjectStrandIdx]);
-                            subjectStrand = str == 1 ? Strand.POSITIVE : Strand.NEGATIVE;
-                        }
-                        catch (NumberFormatException ne) {
-                            throw new RuntimeException("Non-numeric value found in " +
-                                    " either subject strand column");
-                        }
-                    }
-
-                    float percentIden = tabblastn ? Float.parseFloat(tokens[2]) : 100.0f;
-
-                    Block queryBlock = new Block(queryCht, queryStart, queryEnd, queryStrand);
-                    Block subjectBlock = new Block(subjectChr, subjectStart, subjectEnd, subjectStrand);
-
-                    mappings.add(new Mapping(queryBlock, subjectBlock, percentIden));
-                }
-            }
-
-            if (!dataParsed) {
-                throw new RuntimeException("Data not loaded. Number of columns is less than " + nTokens + ".");
-            }
-
-        } catch (Exception ex) {
-            if (lineCount != 0) {
-                throw new ParserException(ex.getMessage(), lineCount, nextLine);
-            } else {
-                throw new RuntimeException(ex);
-            }
-        } finally {
-            if (reader != null) {
-                try {
-                    reader.close();
-
-                } catch (IOException iOException) {
-                }
-            }
-        }
-
-        return mappings;
-    }
-}
diff --git a/src/org/broad/igv/synteny/SyntenyManager.java b/src/org/broad/igv/synteny/SyntenyManager.java
deleted file mode 100644
index 961fbe5..0000000
--- a/src/org/broad/igv/synteny/SyntenyManager.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-
-package org.broad.igv.synteny;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import org.broad.igv.data.BasicScore;
-import org.broad.igv.data.DataUtils;
-import org.broad.igv.data.WiggleDataset;
-import org.broad.igv.data.WiggleParser;
-import org.broad.igv.feature.FeatureUtils;
-import org.broad.igv.util.ResourceLocator;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-
-/**
- * @author jrobinso
- */
-public class SyntenyManager {
-
-
-    /**
-     * Method description
-     *
-     * @param dataset
-     * @param mappings
-     * @param outputFile
-     */
-    public static void MapWigFile(WiggleDataset dataset, List<Mapping> mappings, File outputFile) {
-
-        PrintWriter pw = null;
-
-        try {
-            pw = new PrintWriter(new FileWriter(outputFile));
-
-            // Segregate and sort ortholog
-            Map<String, List<Mapping>> featureMap = new LinkedHashMap();
-            for (Mapping f : mappings) {
-                List<Mapping> fList = featureMap.get(f.getChromosome());
-                if (fList == null) {
-                    fList = new ArrayList();
-                    featureMap.put(f.getChromosome(), fList);
-                }
-                fList.add(f);
-            }
-
-            for (List<Mapping> featureList : featureMap.values()) {
-                FeatureUtils.sortFeatureList(featureList);
-            }
-
-            pw.println("Chr\tStart\tEnd\t\tnucCount (Kwal)");
-            // Loop through chromosomes
-            for (String chr : featureMap.keySet()) {
-                List<Mapping> mappingList = featureMap.get(chr);
-                List<BasicScore> scores = new ArrayList(mappingList.size());
-
-                for (Mapping mapping : mappingList) {
-                    Block queryBlock = mapping.getQueryBlock();
-                    Block subjectBlock = mapping.getSubjectBlock();
-
-                    int[] subjectPosition = dataset.getStartLocations(subjectBlock.getContig());
-                    float[] data = dataset.getData("ignore", subjectBlock.getContig());
-
-                    int s0 = subjectBlock.getStart();
-                    int s1 = subjectBlock.getEnd();
-                    int q0 = queryBlock.getStart();
-                    int q1 = queryBlock.getEnd();
-
-                    int idx0 = DataUtils.getIndexBefore(subjectPosition, Math.min(s0, s1));
-                    int idx1 = DataUtils.getIndexBefore(subjectPosition, Math.max(s0, s1)) + 1;
-                    if (idx1 == subjectPosition.length) {
-                        idx1--;
-                    }
-
-                    double beta = ((double) (q1 - q0)) / (s1 - s0);
-
-                    for (int i = idx0; i <= idx1; i++) {
-                        int pos = (int) (q0 + beta * (subjectPosition[i] - s0));
-                        float d = data[i];
-                        scores.add(new BasicScore(chr, pos, pos + 1, d));
-                    }
-                }
-
-                FeatureUtils.sortFeatureList(scores);
-                for (BasicScore s : scores) {
-                    pw.println(s.getChromosome() + "\t" + s.getStart() + "\t" +
-                            (s.getEnd() + 1) + "\t\t" + s.getScore());
-
-                }
-            }
-        }
-        catch (Exception e) {
-            e.printStackTrace();
-        }
-        finally {
-            pw.close();
-        }
-    }
-
-
-    /**
-     * Method description
-     *
-     * @param args
-     */
-    public static void main(String[] args) {
-
-        String file = "/Users/jrobinso/Alex/Cgla_v_Kwal1.orthologs";
-        String wiggleFile = "/Users/jrobinso/IGVTestData/alex/nucCount_Kwal.wig";
-        File outputFile = new File("/Users/jrobinso/Alex/uncount_Kwal_to_Cgla.igv");
-
-        List<Mapping> mappings = (new MappingParser()).parse(file);
-
-        WiggleDataset ds = (new WiggleParser(new ResourceLocator(wiggleFile), "Kwal")).parse();
-
-        MapWigFile(ds, mappings, outputFile);
-
-    }
-
-
-}
diff --git a/src/org/broad/igv/synteny/SyntenyMapping.java b/src/org/broad/igv/synteny/SyntenyMapping.java
new file mode 100644
index 0000000..b00d048
--- /dev/null
+++ b/src/org/broad/igv/synteny/SyntenyMapping.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.synteny;
+
+/**
+ * User: jrobinso
+ * Date: Feb 19, 2010
+ */ // region R:chr1:chr2:D1         chr1 47243     59894 +   chr2 111274749 111285190 +
+// anchor A:chr1:3928:chr4:34422 chr1 17429161 17429302 + chr4 140098907 140099048 - 96.7
+public class SyntenyMapping {
+
+    private String name;
+    private String fromChr;
+    private int fromStart;
+    private int fromEnd;
+    private String toChr;
+    private int toStart;
+    private int toEnd;
+    private boolean direction;
+    double scaleFactor;
+
+
+    public SyntenyMapping(String name, String fromChr, int fromStart, int fromEnd, String fromDir,
+                          String toChr, int toStart, int toEnd, String toDir) {
+        this.name = name;
+        this.fromChr = fromChr;
+        this.fromStart = fromStart;
+        this.fromEnd = fromEnd;
+        this.toChr = toChr;
+        this.toStart = toStart;
+        this.toEnd = toEnd;
+        this.direction = fromDir.equals(toDir);
+        this.scaleFactor = ((double) (toEnd - toStart)) / (fromEnd - fromStart);
+
+    }
+
+
+    public double mapPosition(int fromPosition) {
+        return (direction == true)
+                ? toStart + scaleFactor * (fromPosition - fromStart) :
+                toEnd - scaleFactor * (fromPosition - fromStart);
+
+    }
+
+    public boolean containsFromPosition(int fromPosition) {
+        return fromPosition >= fromStart && fromPosition < fromEnd;
+    }
+
+    public boolean containsFromInterval(int start, int end) {
+        return end >= fromStart && start < fromEnd;
+    }
+
+
+    public String getName() {
+        return name;
+    }
+
+
+    public String getFromChr() {
+        return fromChr;
+    }
+
+
+    public int getFromStart() {
+        return fromStart;
+    }
+
+
+    public int getFromEnd() {
+        return fromEnd;
+    }
+
+
+    public String getToChr() {
+        return toChr;
+    }
+
+
+    public int getToStart() {
+        return toStart;
+    }
+
+
+    public int getToEnd() {
+        return toEnd;
+    }
+
+
+    public boolean getDirection() {
+        return direction;
+    }
+
+}
diff --git a/src/org/broad/igv/synteny/SyntenyUtils.java b/src/org/broad/igv/synteny/SyntenyUtils.java
index ad28e17..85c1c78 100644
--- a/src/org/broad/igv/synteny/SyntenyUtils.java
+++ b/src/org/broad/igv/synteny/SyntenyUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -24,7 +24,6 @@ package org.broad.igv.synteny;
 import java.io.BufferedReader;
 import java.io.FileReader;
 import java.io.IOException;
-import java.io.PrintWriter;
 import java.util.*;
 import java.util.regex.Pattern;
 
@@ -33,36 +32,17 @@ import java.util.regex.Pattern;
  */
 public class SyntenyUtils {
 
-    static String[] buffer = new String[50];
-    static Map<String, List<Anchor>> anchors;
 
     /**
-     * Method description
-     *
-     * @param args
-     */
-    public static void main(String[] args) {
-        String file = "/Users/jrobinso/mapWithUn_ph_MA4_ML100K.map";
-        //String inputFile = "/Users/jrobinso/IGV/Synteny/ES.H3K4me3.aligned";
-        //String outputFile = "/Users/jrobinso/IGV/Synteny/ES.H3K4me3.hg18_to_mm8.aligned";
-        //loadAnchors(file);
-        //convertAlignments(inputFile, outputFile);
-
-        String inputFile = "/Users/jrobinso/hg18_to_mm8_regions.bed";
-        String outputFile = "/Users/jrobinso/hg18_to_mm8_anchors.bed";
-        convertRegionsToBed(file, inputFile, outputFile);
-
-    }
-
-    /**
-     * Method description
+     * region R:chr1:chr2:D1 chr1 47243 59894 + chr2 111274749 111285190 +
+     * anchor A:chr1:3928:chr4:34422 chr1 17429161 17429302 + chr4 140098907 140099048 - 96.7
      *
      * @param file
      */
-    public static void loadAnchors(String file) {
+    public static Map<String, List<SyntenyMapping>> loadMappings(String file, boolean reverse) {
 
         BufferedReader reader = null;
-        anchors = new HashMap();
+        Map<String, List<SyntenyMapping>> mappings = new HashMap();
 
         try {
             reader = new BufferedReader(new FileReader(file));
@@ -70,44 +50,50 @@ public class SyntenyUtils {
             String nextLine;
             Pattern.compile("\t");
             while ((nextLine = reader.readLine()) != null) {
+                if (!(nextLine.startsWith("region") || nextLine.startsWith("anchor"))) {
+                    continue;
+                }
+                String[] tokens = nextLine.split(" ");
+                String type = tokens[0];
+                String name = tokens[1];
+                String fromChr = tokens[2];
+                int fromStart = Integer.parseInt(tokens[3]);
+                int fromEnd = Integer.parseInt(tokens[4]);
+                String fromStrand = tokens[5];
+                String toChr = tokens[6];
+                int toStart = Integer.parseInt(tokens[7]);
+                int toEnd = Integer.parseInt(tokens[8]);
+                String toStrand = tokens[9];
+
+                SyntenyMapping syntenyMapping = null;
+                if (reverse) {
+                    syntenyMapping = new SyntenyMapping(name, toChr, toStart, toEnd, toStrand,
+                            fromChr, fromStart, fromEnd, fromStrand);
+                } else {
+                    syntenyMapping = new SyntenyMapping(name, fromChr, fromStart, fromEnd, fromStrand,
+                            toChr, toStart, toEnd, toStrand);
+                }
 
-                // region R:chr1:chr2:D1 chr1 47243 59894 + chr2 111274749 111285190 +
-                if (nextLine.startsWith("region")) {
-
-                    // Ignore
-                } else if (nextLine.startsWith("anchor")) {
-
-                    // anchor A:chr1:3928:chr4:34422 chr1 17429161 17429302 + chr4 140098907 140099048 - 96.7
-                    String[] tokens = nextLine.split(" ");
-
-                    boolean forward = tokens[5].trim().equals(tokens[9].trim());
-                    int toStart = Integer.parseInt(tokens[7]);
-                    int toEnd = Integer.parseInt(tokens[8]);
-                    int fromStart = Integer.parseInt(tokens[3]);
-                    int fromEnd = Integer.parseInt(tokens[4]);
-
-                    Anchor anchor = new Anchor(tokens[1] + "(" + tokens[9] + ")", tokens[2],
-                            fromStart, fromEnd, tokens[6], toStart, toEnd,
-                            tokens[9]);
-
-                    List<Anchor> anchorList = anchors.get(anchor.getFromChr());
-                    if (anchorList == null) {
-                        anchorList = new ArrayList(1000);
-                        anchors.put(anchor.getFromChr(), anchorList);
-                    }
-                    anchorList.add(anchor);
-
+                List<SyntenyMapping> syntenyMappingList = mappings.get(syntenyMapping.getFromChr());
+                if (syntenyMappingList == null) {
+                    syntenyMappingList = new ArrayList(1000);
+                    mappings.put(syntenyMapping.getFromChr(), syntenyMappingList);
                 }
+                syntenyMappingList.add(syntenyMapping);
 
             }
 
-            for (List<Anchor> anchorList : anchors.values()) {
-                sortAnchorList(anchorList);
+
+            for (List<SyntenyMapping> syntenyMappingList : mappings.values()) {
+                sortMappingList(syntenyMappingList);
             }
 
+            return mappings;
+
         }
         catch (IOException exception) {
             exception.printStackTrace();
+            return null;
         }
         finally {
             if (reader != null) {
@@ -122,407 +108,71 @@ public class SyntenyUtils {
         }
     }
 
-    /**
-     * Method description
-     *
-     * @param file
-     * @param regionFile
-     * @param anchorFile
-     */
-    public static void convertRegionsToBed(String file, String regionFile, String anchorFile) {
-
-        BufferedReader reader = null;
-        PrintWriter regionWriter = null;
-        PrintWriter anchorWriter = null;
-
-        try {
-            regionWriter = new PrintWriter(regionFile);
-            anchorWriter = new PrintWriter(anchorFile);
-            reader = new BufferedReader(new FileReader(file));
-
-            String nextLine;
-            Pattern.compile("\t");
-            while ((nextLine = reader.readLine()) != null) {
-
-                // region R:chr1:chr2:D1 chr1 47243 59894 + chr2 111274749 111285190 +
-                if (nextLine.startsWith("region")) {
-                    String[] tokens = nextLine.split(" ");
-                    regionWriter.println(tokens[6] + "\t" + tokens[7] + "\t" + tokens[8] + "\t"
-                            + tokens[1] + "\t\t" + tokens[9]);
-                } else if (nextLine.startsWith("anchor")) {
-
-                    // anchor A:chr1:3928:chr4:34422 chr1 17429161 17429302 + chr4 140098907 140099048 - 96.7
-                    String[] tokens = nextLine.split(" ");
-
-                    boolean forward = tokens[5].trim().equals(tokens[9].trim());
-                    int toStart = Integer.parseInt(tokens[7]);
-                    int toEnd = Integer.parseInt(tokens[8]);
-                    int fromStart = Integer.parseInt(tokens[3]);
-                    int fromEnd = Integer.parseInt(tokens[4]);
-
-                    Anchor anchor = new Anchor(tokens[1] + "(" + tokens[9] + ")", tokens[2],
-                            fromStart, fromEnd, tokens[6], toStart, toEnd,
-                            tokens[9]);
 
-                    // Center anchor
-                    int start = anchor.mapPosition(fromStart);
+    public static SyntenyMapping getMappingContaining(List<SyntenyMapping> mappings, int fromPosition) {
 
-                    // Align at end
-                    // int start = (forward ? toStart : toEnd - fromSize);
-                    int end = anchor.mapPosition(fromEnd);
-
-                    anchorWriter.println(anchor.getToChr() + "\t" + start + "\t" + end + "\t"
-                            + anchor.getName() + "\t\t" + anchor.getDirection());
-                }
-
-
-            }
-
-
-        }
-        catch (IOException exception) {
-            exception.printStackTrace();
-        }
-        finally {
-            if (reader != null) {
-                try {
-                    regionWriter.close();
-                    anchorWriter.close();
-                    reader.close();
-
-                }
-                catch (IOException iOException) {
-                }
+        int idx = getIndexBefore(mappings, fromPosition);
+        for (int i = idx; i < mappings.size(); i++) {
+            SyntenyMapping mapping = mappings.get(i);
+            if (mapping.containsFromPosition(fromPosition)) {
+                return mapping;
             }
         }
-
+        return null;
     }
 
-    /**
-     * Method description
-     *
-     * @param inputFile
-     * @param outputFile
-     */
-    public static void convertAlignments(String inputFile, String outputFile) {
-
-        BufferedReader reader = null;
-        PrintWriter writer = null;
-
-        try {
-            writer = new PrintWriter(outputFile);
-            reader = new BufferedReader(new FileReader(inputFile));
-
-            String nextLine;
-            while ((nextLine = reader.readLine()) != null) {
-
-                // chr15     62533608        62533644        -       2041T.3.1       4
-                String[] tokens = nextLine.split("\t");
-
-                String chr = tokens[0].trim();
-                int fromStart = Integer.parseInt(tokens[1]);
-                int fromEnd = Integer.parseInt(tokens[2]);
-                String fromStrand = tokens[3].trim();
-
-                // Extend alignment -> fragment length
-                if (fromStrand.equals("+")) {
-                    fromEnd += 250;
-                } else {
-                    fromStart += 250;
-                }
-
-                Alignment alignment = new Alignment(chr, fromStart, fromEnd, fromStrand);
-
-                Anchor anchor = getAnchorContaining(alignment);
-                if (anchor != null) {
-                    String mappedStrand = alignment.getStrand();
-                    if (!anchor.getDirection()) {
-                        mappedStrand = mappedStrand.equals("+") ? "-" : "+";
-
-                    }
-                    int mappedStart = anchor.mapPosition(fromStart);
-                    int mappedEnd = anchor.mapPosition(fromEnd);
-                    writer.println(
-                            anchor.getToChr() + "\t" + mappedStart + "\t"
-                                    + mappedEnd + "\t" + mappedStrand);
-                }
-
+    public static List<SyntenyMapping> getMappingsOverlapping(List<SyntenyMapping> mappings, int fromStart, int fromEnd) {
 
+        int i1 = -1;
+        int i2 = -1;
+        int idx = getIndexBefore(mappings, fromStart);
+        for (int i = idx; i < mappings.size(); i++) {
+            SyntenyMapping mapping = mappings.get(i);
+            if (mapping.containsFromPosition(fromStart)) {
+                i1 = idx;
+                break;
             }
-
-
-        }
-        catch (Exception exception) {
-            exception.printStackTrace();
-        }
-        finally {
-            if (reader != null) {
-                try {
-                    writer.close();
-                    reader.close();
-
-                }
-                catch (IOException iOException) {
-                }
+            else if(mapping.getFromStart() > fromStart) {
+                i1 = idx;
+                break;
             }
         }
-
-    }
-
-
-    // chr15     62533608        62533644        -       2041T.3.1       4
-    static class Alignment {
-
-        String chr;
-        int start;
-        int end;
-        String strand;
-
-        /**
-         * Constructs ...
-         *
-         * @param chr
-         * @param start
-         * @param end
-         * @param strand
-         */
-        public Alignment(String chr, int start, int end, String strand) {
-            this.chr = chr;
-            this.start = start;
-            this.end = end;
-            this.strand = strand;
-        }
-
-        /**
-         * Method description
-         *
-         * @return
-         */
-        public String getChr() {
-            return chr;
-        }
-
-        /**
-         * Method description
-         *
-         * @return
-         */
-        public int getEnd() {
-            return end;
-        }
-
-        /**
-         * Method description
-         *
-         * @return
-         */
-        public int getStart() {
-            return start;
-        }
-
-        /**
-         * Method description
-         *
-         * @return
-         */
-        public String getStrand() {
-            return strand;
-        }
-
-
-    }
-
-
-    // anchor A:chr1:3928:chr4:34422 chr1 17429161 17429302 + chr4 140098907 140099048 - 96.7
-    static class Anchor {
-
-        private String name;
-        private String fromChr;
-        private int fromStart;
-        private int fromEnd;
-        private String toChr;
-        private int toStart;
-        private int toEnd;
-        private boolean direction;
-
-        int offset;
-
-        /**
-         * Constructs ...
-         *
-         * @param name
-         * @param fromChr
-         * @param fromStart
-         * @param fromEnd
-         * @param toChr
-         * @param toStart
-         * @param toEnd
-         * @param dir
-         */
-        public Anchor(String name, String fromChr, int fromStart, int fromEnd, String toChr,
-                      int toStart, int toEnd, String dir) {
-            this.name = name;
-            this.fromChr = fromChr;
-            this.fromStart = fromStart;
-            this.fromEnd = fromEnd;
-            this.toChr = toChr;
-            this.toStart = toStart;
-            this.toEnd = toEnd;
-            this.direction = dir.equals("+");
-
-            // Compute offset to center anchor
-            int fromSize = Math.abs(fromEnd - fromStart);
-            int toSize = Math.abs(toEnd - toStart);
-            offset = (fromSize - toSize) / 2;
-        }
-
-        /**
-         * Method description
-         *
-         * @param fromPosition
-         * @return
-         */
-        public int mapPosition(int fromPosition) {
-
-            return (direction == true)
-                    ? toStart + offset + (fromPosition - fromStart)
-                    : toEnd - (offset + (fromPosition - fromStart));
-        }
-
-        /**
-         * Method description
-         *
-         * @return
-         */
-        public String getName() {
-            return name;
-        }
-
-        /**
-         * Method description
-         *
-         * @return
-         */
-        public String getFromChr() {
-            return fromChr;
-        }
-
-        /**
-         * Method description
-         *
-         * @return
-         */
-        public int getFromStart() {
-            return fromStart;
-        }
-
-        /**
-         * Method description
-         *
-         * @return
-         */
-        public int getFromEnd() {
-            return fromEnd;
-        }
-
-        /**
-         * Method description
-         *
-         * @return
-         */
-        public String getToChr() {
-            return toChr;
-        }
-
-        /**
-         * Method description
-         *
-         * @return
-         */
-        public int getToStart() {
-            return toStart;
-        }
-
-        /**
-         * Method description
-         *
-         * @return
-         */
-        public int getToEnd() {
-            return toEnd;
-        }
-
-        /**
-         * Method description
-         *
-         * @return
-         */
-        public boolean getDirection() {
-            return direction;
+        idx = getIndexBefore(mappings, fromEnd);
+        for (int i = idx; i < mappings.size(); i++) {
+            SyntenyMapping mapping = mappings.get(i);
+            if (mapping.containsFromPosition(fromEnd)) {
+                i2 = idx;
+                break;
+            }
+            else if(mapping.getFromStart() > fromStart) {
+                i2 = Math.max(i1, idx - 1);
+                break;
+            }
         }
 
-        /**
-         * Method description
-         *
-         * @param alignment
-         * @return
-         */
-        public boolean overlaps(Alignment alignment) {
-            return alignment.getChr().equals(getFromChr())
-                    && ((alignment.getStart() >= getFromStart() && alignment.getStart() <= getFromEnd()) ||
-                    (alignment.getEnd() >= getFromStart() && alignment.getEnd() <= getFromEnd()) ||
-                    (alignment.getStart() < getFromStart() && alignment.getEnd() > getFromEnd()));
+        if(i1 < 0 || i2  < 0) {
+            return null;
         }
+        return mappings.subList(i1, i2+1);
     }
 
-
     /**
      * Sort the feature list by ascending start value
      *
      * @param features
      */
-    public static void sortAnchorList(List<Anchor> features) {
+    private static void sortMappingList(List<SyntenyMapping> features) {
 
-        Collections.sort(features, new Comparator<Anchor>() {
+        Collections.sort(features, new Comparator<SyntenyMapping>() {
 
-            public int compare(Anchor o1, Anchor o2) {
+            public int compare(SyntenyMapping o1, SyntenyMapping o2) {
 
                 return (int) (o1.getFromStart() - o2.getFromStart());
             }
         });
     }
 
-    /**
-     * Method description
-     *
-     * @param alignment
-     * @return
-     */
-    static Anchor getAnchorContaining(Alignment alignment) {
-        List<Anchor> anchorList = anchors.get(alignment.getChr());
-        if (anchorList != null) {
-            int startIdx = getIndexBefore(anchorList, alignment.getStart());
-            for (int i = startIdx; i < anchorList.size(); i++) {
-                Anchor a = anchorList.get(i);
-                if (a.getFromStart() > alignment.getEnd()) {
-                    break;
-                } else if (a.overlaps(alignment)) {
-                    return a;
-                }
-            }
-        }
-        return null;
-
-    }
-
-    /**
-     * Method description
-     *
-     * @param values
-     * @param x
-     * @return
-     */
-    static int getIndexBefore(List<Anchor> values, int x) {
+    private static int getIndexBefore(List<SyntenyMapping> values, int x) {
         return getIndexBefore(values, x, 0, values.size());
     }
 
@@ -535,7 +185,7 @@ public class SyntenyUtils {
      * @param rightBound
      * @return
      */
-    static int getIndexBefore(List<Anchor> values, int x, int leftBound, int rightBound) {
+    private static int getIndexBefore(List<SyntenyMapping> values, int x, int leftBound, int rightBound) {
 
         int idx = (leftBound + rightBound) / 2;
 
diff --git a/src/org/broad/igv/tdf/Bin.java b/src/org/broad/igv/tdf/Bin.java
index 927766f..41c0a7e 100644
--- a/src/org/broad/igv/tdf/Bin.java
+++ b/src/org/broad/igv/tdf/Bin.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -18,7 +18,7 @@
 
 package org.broad.igv.tdf;
 
-import org.broad.igv.data.FloatArrayList;
+import org.broad.igv.util.collections.FloatArrayList;
 import org.broad.igv.feature.LocusScore;
 import org.broad.igv.tools.Accumulator;
 import org.broad.igv.track.WindowFunction;
@@ -33,11 +33,11 @@ import java.util.List;
  * Date: Dec 18, 2009
  * Time: 11:22:54 PM
  * To change this template use File | Settings | File Templates.
- */ // TODO -- window function
+ */
 public class Bin implements LocusScore {
 
     private int start;
-    int end;
+    private int end;
     private float score = Float.MIN_VALUE;
 
     Accumulator accumulator;
@@ -52,7 +52,6 @@ public class Bin implements LocusScore {
         this.values = new FloatArrayList(maxValues);
         this.accumulator = new Accumulator(Arrays.asList(windowFunction));
         this.windowFunction = windowFunction;
-
         addValue(probeName, initialValue);
     }
 
@@ -71,18 +70,12 @@ public class Bin implements LocusScore {
     }
 
     public boolean isExtension(Bin bin) {
-        return (end == bin.start) && score == bin.getScore();
-    }
-
-
-    private boolean fpEqual(float v1, float v2, double epsilon) {
-        return v1 > (v2 - epsilon) &&  v1 < (v2 + epsilon);
-
+        return (end == bin.start) && getScore() == bin.getScore();
     }
 
 
     public void addValue(String name, float value) {
-        if (values.getSize() < maxValues) {
+        if (values.size() < maxValues) {
             if (name != null) {
                 if (names == null) {
                     names = new ArrayList<String>(maxValues);
@@ -95,6 +88,10 @@ public class Bin implements LocusScore {
     }
 
 
+    public String getChr() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
     public int getStart() {
         return start;  //To change body of implemented methods use File | Settings | File Templates.
     }
@@ -143,7 +140,7 @@ public class Bin implements LocusScore {
         StringBuffer sb = new StringBuffer(50);
         sb.append("Value: ");
         sb.append(String.valueOf(getScore()));
-        if (values.getSize() == 1) {
+        if (values.size() == 1) {
             if (names != null && names.size() > 0) {
                 sb.append(" (");
                 sb.append(names.get(0));
@@ -152,7 +149,7 @@ public class Bin implements LocusScore {
         } else {
             sb.append("<br> ");
             sb.append(windowFunction.getDisplayName() + " of " +
-                    (values.getSize() == maxValues ? "> " : "") + values.getSize() + " values:");
+                    (values.size() == maxValues ? "> " : "") + values.size() + " values:");
             float[] v = values.toArray();
             for (int i = 0; i < v.length; i++) {
                 sb.append("<br>   " + v[i]);
@@ -170,7 +167,4 @@ public class Bin implements LocusScore {
         return sb.toString();
     }
 
-    public void setScore(float score) {
-        this.score = score;
-    }
 }
diff --git a/src/org/broad/igv/tdf/BufferedByteWriter.java b/src/org/broad/igv/tdf/BufferedByteWriter.java
new file mode 100644
index 0000000..e9dd2f4
--- /dev/null
+++ b/src/org/broad/igv/tdf/BufferedByteWriter.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.tdf;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/*
+ * 
+ */
+public class BufferedByteWriter {
+
+    /**
+     * Reports the total number of bytes written
+     */
+
+    ByteArrayOutputStream buffer;
+
+
+    public BufferedByteWriter() {
+        this(8192);
+    }
+
+
+    public BufferedByteWriter(int size) {
+        if (size <= 0) {
+            throw new IllegalArgumentException("Buffer size <= 0");
+        }
+        buffer = new ByteArrayOutputStream(size);
+    }
+
+    public byte [] getBytes() {
+        return buffer.toByteArray();
+    }
+
+    public int bytesWritten() {
+        return buffer.size();
+    }
+
+    /**
+     * Writes the specified byte to this buffered output stream.
+     *
+     * @param b the byte to be written.
+     * @throws IOException if an I/O error occurs.
+     */
+    public void put(int b) throws IOException {
+        buffer.write(b);
+    }
+
+    public void put(byte b[]) throws IOException {
+        buffer.write(b);
+    }
+
+    /**
+     * Writes <code>len</code> bytes from the specified byte array
+     * starting at offset <code>off</code> to this buffered output stream.
+     *
+     * @param b   the data.
+     * @param off the start offset in the data.
+     * @param len the number of bytes to write.
+     * @throws IOException if an I/O error occurs.
+     */
+    public void put(byte b[], int off, int len) throws IOException {
+        buffer.write(b, off, len);
+    }
+
+    public void putInt(int v) throws IOException {
+        buffer.write((v >>> 0) & 0xFF);
+        buffer.write((v >>> 8) & 0xFF);
+        buffer.write((v >>> 16) & 0xFF);
+        buffer.write((v >>> 24) & 0xFF);
+    }
+
+    public void putFloat(float f) throws IOException {
+        int v = Float.floatToIntBits(f);
+        putInt(v);
+    }
+
+    /**
+     * Writes a <code>long</code> to the underlying output stream as eight
+     * bytes, high byte first. In no exception is thrown, the counter
+     * <code>written</code> is incremented by <code>8</code>.
+     *
+     * @param v a <code>long</code> to be written.
+     * @throws IOException if an I/O error occurs.
+     * @see java.io.FilterOutputStream#out
+     */
+    public void putLong(long v) throws IOException {
+        buffer.write((byte) (v >>> 0));
+        buffer.write((byte) (v >>> 8));
+        buffer.write((byte) (v >>> 16));
+        buffer.write((byte) (v >>> 24));
+        buffer.write((byte) (v >>> 32));
+        buffer.write((byte) (v >>> 40));
+        buffer.write((byte) (v >>> 48));
+        buffer.write((byte) (v >>> 56));
+    }
+
+    public void putNullTerminatedString(String s) throws IOException {
+        buffer.write(s.getBytes());
+        buffer.write((byte) 0);
+    }
+
+}
diff --git a/src/org/broad/igv/tdf/IBFAttributes.java b/src/org/broad/igv/tdf/IBFAttributes.java
index a6fdd50..0e2b294 100644
--- a/src/org/broad/igv/tdf/IBFAttributes.java
+++ b/src/org/broad/igv/tdf/IBFAttributes.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/tdf/IBFBin.java b/src/org/broad/igv/tdf/IBFBin.java
index ac35e84..ab74c9b 100644
--- a/src/org/broad/igv/tdf/IBFBin.java
+++ b/src/org/broad/igv/tdf/IBFBin.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/tdf/IBFDataset.java b/src/org/broad/igv/tdf/IBFDataset.java
deleted file mode 100644
index fda7b90..0000000
--- a/src/org/broad/igv/tdf/IBFDataset.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.tdf;
-
-import org.broad.igv.util.LRUCache;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Represents the data for a particular chromosome and zoom level
- *
- * @author jrobinso
- */
-public class IBFDataset extends IBFEntity {
-
-    public enum DataType {
-
-        BYTE, SHORT, INT, FLOAT, DOUBLE, STRING
-    }
-
-    ;
-    DataType dataType;
-    int tileWidth;
-    long[] tilePositions;
-    int[] tileSizes;
-    int nTiles;
-    LRUCache<String, IBFTile> cache = new LRUCache(20);
-    // TODO -- refactor this dependency out
-    TDFReader reader;
-
-    public IBFDataset(String name, DataType dataType, int tileWidth, int nTiles) {
-        super(name);
-        this.dataType = dataType;
-        this.tileWidth = tileWidth;
-        this.nTiles = nTiles;
-        this.tilePositions = new long[nTiles];
-        this.tileSizes = new int[nTiles];
-
-        // Initialize tile positions to -1.  This indicates a blank tile and is the default
-        Arrays.fill(tilePositions, -1);
-
-    }
-
-    public IBFDataset(String name, LEDataInputStream inputStream, TDFReader reader) throws IOException {
-        super(name);
-        this.reader = reader;
-        fill(inputStream);
-    }
-
-    public void write(LEBufferedDataOutputStream dos) throws IOException {
-
-        writeAttributes(dos);
-
-        writeString(dos, dataType.toString());
-        dos.writeFloat(tileWidth);
-        //       dos.writeFloat(binWidth);
-        dos.writeInt(tilePositions.length);
-        for (int i = 0; i < tilePositions.length; i++) {
-            dos.writeLong(tilePositions[i]);
-            dos.writeInt(tileSizes[i]);
-        }
-
-    }
-
-    private void fill(LEDataInputStream dis) throws IOException {
-
-        // Attributes
-        readAttributes(dis);
-
-        dataType = IBFDataset.DataType.valueOf(dis.readString());
-
-        // TODO -- change tileWidth to int ?
-        tileWidth = (int) dis.readFloat();
-
-
-        nTiles = dis.readInt();
-        tilePositions = new long[nTiles];
-        tileSizes = new int[nTiles];
-
-        for (int i = 0; i < nTiles; i++) {
-            tilePositions[i] = dis.readLong();
-            tileSizes[i] = dis.readInt();
-        }
-
-    }
-
-    // TODO -- this uses an implied linear index.  Abstract index or replace
-    // with general interval index
-    // TODO -- gather all non-cached tiles and read in one chunk.  See BAM
-    // alignment reader class
-    public List<IBFTile> getTiles(int startLocation, int endLocation) {
-
-        List<IBFTile> tiles = new ArrayList();
-        int startTile = (int) (startLocation / tileWidth);
-        int endTile = (int) (endLocation / tileWidth);
-        for (int t = startTile; t <= endTile; t++) {
-            IBFTile tile = getTile(t);
-            if (tile != null && tile.getSize() > 0) {
-                tiles.add(tile);
-            }
-        }
-        return tiles;
-
-    }
-
-    // IBFTile computeTile(IBFDataset ds, int t, List<LocusScore> scores, String chr)
-    synchronized IBFTile getTile(int t) {
-        String key = getName() + "_" + t;
-
-        IBFTile tile = null;
-        if (!cache.containsKey(key)) {
-            tile = reader.readTile(this, t);
-            cache.put(key, tile);
-        } else {
-            tile = cache.get(key);
-        }
-        return tile;
-    }
-
-}
diff --git a/src/org/broad/igv/tdf/IBFEntity.java b/src/org/broad/igv/tdf/IBFEntity.java
deleted file mode 100644
index e73d927..0000000
--- a/src/org/broad/igv/tdf/IBFEntity.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.tdf;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * @author jrobinso
- */
-public class IBFEntity {
-
-    Map<String, String> attributes;
-    private String name;
-
-    public IBFEntity(String name) {
-        this.name = name;
-        this.attributes = new HashMap();
-    }
-
-    public IBFEntity(String name, Map<String, String> attributes) {
-        this.name = name;
-        this.attributes = attributes;
-    }
-
-    public Set<String> getAttributeNames() {
-        return attributes.keySet();
-    }
-
-    public String getAttribute(String name) {
-        return attributes.get(name);
-    }
-
-    public void setAttribute(String name, String value) {
-        attributes.put(name, value);
-    }
-
-    void writeAttributes(LEBufferedDataOutputStream dos) throws IOException {
-        dos.writeInt(attributes.size());
-        for (Map.Entry<String, String> entry : attributes.entrySet()) {
-            writeString(dos, entry.getKey());
-            writeString(dos, entry.getValue());
-        }
-    }
-
-    void readAttributes(LEDataInputStream dis) throws IOException {
-        int nAttributes = dis.readInt();
-        while (nAttributes-- > 0) {
-            String key = dis.readString();
-            String value = dis.readString();
-            setAttribute(key, value);
-        }
-    }
-
-    /**
-     * Write a string as a null terminated character sequence.
-     * <p/>
-     * IGV requires all strings to be ascii,  so single byte storaged is enough
-     *
-     * @param dos
-     * @param s
-     * @throws java.io.IOException
-     */
-    public void writeString(LEBufferedDataOutputStream dos, String s) throws IOException {
-        dos.write(s.getBytes());
-        dos.writeByte(0);
-    }
-
-    /**
-     * @return the name
-     */
-    public String getName() {
-        return name;
-    }
-}
diff --git a/src/org/broad/igv/tdf/IBFFixedTile.java b/src/org/broad/igv/tdf/IBFFixedTile.java
deleted file mode 100644
index ca2f5e8..0000000
--- a/src/org/broad/igv/tdf/IBFFixedTile.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.tdf;
-
-import java.io.DataInput;
-import java.io.EOFException;
-import java.io.IOException;
-
-/**
- * @author jrobinso
- */
-public class IBFFixedTile implements IBFTile {
-
-    int tileStart;
-    float span;
-    int start;
-    float[][] data;
-
-    public IBFFixedTile(DataInput inputStream, int nSamples) throws IOException {
-        this.fill(inputStream, nSamples);
-    }
-
-    public IBFFixedTile(int tileStart, int start, float span, float[][] data) {
-        this.tileStart = tileStart;
-        this.span = span;
-        this.data = data;
-        this.start = start;
-    }
-
-    public int getTileStart() {
-        return start;
-    }
-
-    public int getTileEnd() {
-        return getSize() == 0 ? 0 : getEndPosition(getSize() - 1);
-    }
-
-    public int getStartPosition(int idx) {
-        return start + (int) (idx * span);
-    }
-
-    public int getEndPosition(int idx) {
-        return start + (int) ((idx + 1) * span);
-    }
-
-    public String getName(int idx) {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
-    }
-
-    public float getValue(int row, int idx) {
-        return data[row][idx];
-    }
-
-    public int getSize() {
-        return (data == null ? 0 : data[0].length);
-    }
-
-
-    // TODO -- record "type",  extent (longest feature), other stuff
-    public void writeTo(LEBufferedDataOutputStream fos) throws IOException {
-
-        fos.writeString(IBFTile.Type.fixedStep.toString());
-        fos.writeInt(getSize());
-        fos.writeInt(start);
-        fos.writeFloat(span);
-        for (int i = 0; i < data.length; i++) {
-            for (int j = 0; j < data[i].length; j++) {
-                fos.writeFloat(data[i][j]);
-            }
-        }
-
-    }
-
-    public void fill(DataInput dis, int nSamples) throws IOException {
-
-        int nPositions = dis.readInt();
-        start = dis.readInt();
-        span = dis.readFloat();
-
-        data = new float[nSamples][nPositions];
-        for (int sample = 0; sample < nSamples; sample++) {
-            data[sample] = new float[nPositions];
-            for (int i = 0; i < nPositions; i++) {
-                try {
-                    data[sample][i] = dis.readFloat();
-                } catch (EOFException ex) {
-                    ex.printStackTrace();
-                }
-            }
-        }
-
-    }
-}
diff --git a/src/org/broad/igv/tdf/IBFGroup.java b/src/org/broad/igv/tdf/IBFGroup.java
deleted file mode 100644
index 4723892..0000000
--- a/src/org/broad/igv/tdf/IBFGroup.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.tdf;
-
-import java.io.IOException;
-import java.util.Map;
-
-/**
- * @author jrobinso
- */
-public class IBFGroup extends IBFEntity {
-
-    IBFGroup(String name) {
-        super(name);
-    }
-
-    IBFGroup(String name, Map<String, String> attributes) {
-        super(name, attributes);
-    }
-
-    public IBFGroup(String name, LEDataInputStream inputStream) throws IOException {
-        super(name);
-        fill(inputStream);
-    }
-
-    void write(LEBufferedDataOutputStream dos) throws IOException {
-
-        writeAttributes(dos);
-    }
-
-    void fill(LEDataInputStream dis) throws IOException {
-        readAttributes(dis);
-    }
-}
diff --git a/src/org/broad/igv/tdf/IBFIndex.java b/src/org/broad/igv/tdf/IBFIndex.java
index ac17fd7..23ef905 100644
--- a/src/org/broad/igv/tdf/IBFIndex.java
+++ b/src/org/broad/igv/tdf/IBFIndex.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/tdf/IBFTile.java b/src/org/broad/igv/tdf/IBFTile.java
deleted file mode 100644
index 128548a..0000000
--- a/src/org/broad/igv/tdf/IBFTile.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-package org.broad.igv.tdf;
-
-import java.io.IOException;
-
-/**
- * @author jrobinso
- */
-public interface IBFTile {
-
-    enum Type {
-        fixedStep, variableStep, bed, bedWithName
-    }
-
-    ;
-
-    public int getTileStart();
-
-    public int getSize();
-
-    public int getStartPosition(int idx);
-
-    public int getEndPosition(int idx);
-
-    public String getName(int idx);
-
-    public float getValue(int row, int idx);
-
-    public void writeTo(LEBufferedDataOutputStream fos) throws IOException;
-
-}
diff --git a/src/org/broad/igv/tdf/IBFVaryTile.java b/src/org/broad/igv/tdf/IBFVaryTile.java
deleted file mode 100644
index ee1be51..0000000
--- a/src/org/broad/igv/tdf/IBFVaryTile.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.tdf;
-
-import java.io.DataInput;
-import java.io.IOException;
-
-/**
- * @author jrobinso
- */
-public class IBFVaryTile implements IBFTile {
-
-    int tileStart;
-    float span;
-    int[] start;
-    float[][] data;
-
-    public IBFVaryTile(DataInput inputStream, int nSamples) throws IOException {
-        this.fill(inputStream, nSamples);
-    }
-
-    public IBFVaryTile(int tileStart, float span, int[] start, float[][] data) {
-        this.tileStart = tileStart;
-        this.span = span;
-        this.start = start;
-        this.data = data;
-    }
-
-
-    public int getSize() {
-        return start.length;
-    }
-
-    public int getTileStart() {
-        return tileStart;
-    }
-
-    public int getTileEnd() {
-        return getSize() == 0 ? 0 : getEndPosition(getSize() - 1);
-    }
-
-    public int getStartPosition(int idx) {
-        return start[idx];
-    }
-
-    public int getEndPosition(int idx) {
-        return (int) (start[idx] + span);
-    }
-
-    public String getName(int idx) {
-        return null;
-    }
-
-    public float getValue(int row, int idx) {
-        return data[row][idx];
-    }
-
-    // TODO -- record "type",  extent (longest feature), other stuff
-    public void writeTo(LEBufferedDataOutputStream fos) throws IOException {
-
-        // File type
-        fos.writeString(IBFTile.Type.variableStep.toString());
-
-        fos.writeInt(tileStart);
-        fos.writeFloat(span);
-
-        int nPositions = start.length;
-        int nSamples = data.length;
-
-        fos.writeInt(nPositions);
-
-        for (int i = 0; i < nPositions; i++) {
-            fos.writeInt(start[i]);
-        }
-
-        fos.writeInt(nSamples);
-        for (int i = 0; i < data.length; i++) {
-            for (int j = 0; j < data[i].length; j++) {
-                fos.writeFloat(data[i][j]);
-            }
-        }
-    }
-
-    public void fill(DataInput dis, int nSamples) throws IOException {
-
-        tileStart = dis.readInt();
-        span = dis.readFloat();
-
-        int nPositions = dis.readInt();
-        start = new int[nPositions];
-        for (int i = 0; i < nPositions; i++) {
-            start[i] = dis.readInt();
-        }
-
-        int nS = dis.readInt();
-        assert (nS == nSamples);
-
-        data = new float[nS][nPositions];
-        for (int row = 0; row < nS; row++) {
-            data[row] = new float[nPositions];
-            for (int i = 0; i < nPositions; i++) {
-                data[row][i] = dis.readFloat();
-            }
-        }
-
-    }
-}
diff --git a/src/org/broad/igv/tdf/LEBufferedDataOutputStream.java b/src/org/broad/igv/tdf/LEBufferedDataOutputStream.java
deleted file mode 100644
index 65326b5..0000000
--- a/src/org/broad/igv/tdf/LEBufferedDataOutputStream.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * The Broad Institute
- * SOFTWARE COPYRIGHT NOTICE AGREEMENT
- * This is copyright (2007-2009) by the Broad Institute/Massachusetts Institute
- * of Technology.  It is licensed to You under the Gnu Public License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
- *
- *    http://www.opensource.org/licenses/gpl-2.0.php
- *
- * This software is supplied without any warranty or guaranteed support
- * whatsoever. Neither the Broad Institute nor MIT can be responsible for its
- * use, misuse, or functionality.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.tdf;
-
-import java.io.DataOutput;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/*
- * 
- */
-public class LEBufferedDataOutputStream extends java.io.BufferedOutputStream implements DataOutput {
-
-    /**
-     * Reports the total number of bytes written
-     */
-    private long bytesWritten = 0;
-
-    /**
-     * Creates a new buffered output stream to write data to the
-     * specified underlying output stream.
-     *
-     * @param out the underlying output stream.
-     */
-    public LEBufferedDataOutputStream(OutputStream out) {
-        this(out, 8192);
-    }
-
-
-    /**
-     * Creates a new buffered output stream to write data to the
-     * specified underlying output stream with the specified buffer
-     * size.
-     *
-     * @param out  the underlying output stream.
-     * @param size the buffer size.
-     * @throws IllegalArgumentException if size &lt;= 0.
-     */
-    public LEBufferedDataOutputStream(OutputStream out, int size) {
-        super(out);
-        if (size <= 0) {
-            throw new IllegalArgumentException("Buffer size <= 0");
-        }
-        buf = new byte[size];
-    }
-
-    /**
-     * Writes the specified byte to this buffered output stream.
-     *
-     * @param b the byte to be written.
-     * @throws IOException if an I/O error occurs.
-     */
-    public synchronized void write(int b) throws IOException {
-        super.write(b);
-        bytesWritten++;
-    }
-
-    /**
-     * Writes <code>len</code> bytes from the specified byte array
-     * starting at offset <code>off</code> to this buffered output stream.
-     *
-     * @param b   the data.
-     * @param off the start offset in the data.
-     * @param len the number of bytes to write.
-     * @throws IOException if an I/O error occurs.
-     */
-    public synchronized void write(byte b[], int off, int len) throws IOException {
-        super.write(b, off, len);
-        bytesWritten += len;
-    }
-
-    public synchronized long bytesWritten() {
-        return bytesWritten;
-    }
-
-    public void writeInt(int v) throws IOException {
-        write((v >>> 0) & 0xFF);
-        write((v >>> 8) & 0xFF);
-        write((v >>> 16) & 0xFF);
-        write((v >>> 24) & 0xFF);
-    }
-
-    public void writeFloat(float f) throws IOException {
-        int v = Float.floatToIntBits(f);
-        writeInt(v);
-    }
-
-    /**
-     * Writes a <code>long</code> to the underlying output stream as eight
-     * bytes, high byte first. In no exception is thrown, the counter
-     * <code>written</code> is incremented by <code>8</code>.
-     *
-     * @param v a <code>long</code> to be written.
-     * @throws IOException if an I/O error occurs.
-     * @see java.io.FilterOutputStream#out
-     */
-    public void writeLong(long v) throws IOException {
-        write((byte) (v >>> 0));
-        write((byte) (v >>> 8));
-        write((byte) (v >>> 16));
-        write((byte) (v >>> 24));
-        write((byte) (v >>> 32));
-        write((byte) (v >>> 40));
-        write((byte) (v >>> 48));
-        write((byte) (v >>> 56));
-    }
-
-    public void writeString(String s) throws IOException {
-        write(s.getBytes());
-        writeByte(0);
-    }
-
-    public void writeBoolean(boolean v) throws IOException {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    public void writeByte(int v) throws IOException {
-        write(v);
-    }
-
-    public void writeShort(int v) throws IOException {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    public void writeChar(int v) throws IOException {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    public void writeDouble(double v) throws IOException {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    public void writeBytes(String s) throws IOException {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    public void writeChars(String s) throws IOException {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    public void writeUTF(String str) throws IOException {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-}
diff --git a/src/org/broad/igv/tdf/LEDataInputStream.java b/src/org/broad/igv/tdf/LEDataInputStream.java
deleted file mode 100644
index 7524410..0000000
--- a/src/org/broad/igv/tdf/LEDataInputStream.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * @(#)LEDataInputStream.java
- *
- * Summary: Little-Endian version of DataInputStream.
- *
- * Copyright: (c) 1998-2009 Roedy Green, Canadian Mind Products, http://mindprod.com
- *
- * Licence: This software may be copied and used freely for any purpose but military.
- *          http://mindprod.com/contact/nonmil.html
- *
- * Requires: JDK 1.1+
- *
- * Created with: IntelliJ IDEA IDE.
- *
- * Version History:
- *  1.8 2007-05-24
- */
-package org.broad.igv.tdf;
-
-import cern.colt.list.ByteArrayList;
-
-import java.io.DataInput;
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Little-Endian version of DataInputStream.
- * <p/>
- * Very similar to DataInputStream except it reads
- * little-endian instead of big-endian binary data. We can't extend
- * DataInputStream directly since it has only final methods, though
- * DataInputStream itself is not final. This forces us implement
- * LEDataInputStream with a DataInputStream object, and use wrapper methods.
- *
- * @author Roedy Green, Canadian Mind Products
- * @version 1.8 2007-05-24
- * @since 1998
- */
-public final class LEDataInputStream implements DataInput {
-    // ------------------------------ CONSTANTS ------------------------------
-
-    /**
-     * undisplayed copyright notice.
-     *
-     * @noinspection UnusedDeclaration
-     */
-    private static final String EMBEDDED_COPYRIGHT =
-            "copyright (c) 1999-2009 Roedy Green, Canadian Mind Products, http://mindprod.com";
-    // ------------------------------ FIELDS ------------------------------
-    /**
-     * to get at the big-Endian methods of a basic DataInputStream
-     *
-     * @noinspection WeakerAccess
-     */
-    protected final DataInputStream dis;
-    /**
-     * to get at the a basic readBytes method.
-     *
-     * @noinspection WeakerAccess
-     */
-    protected final InputStream is;
-    /**
-     * work array for buffering input.
-     *
-     * @noinspection WeakerAccess
-     */
-    protected final byte[] work;
-
-    // -------------------------- PUBLIC STATIC METHODS --------------------------
-    public String readString() throws IOException {
-        ByteArrayList bytes = new ByteArrayList(100);
-        byte b = -1;
-        while ((b = readByte()) != 0) {
-            bytes.add(b);
-        }
-        bytes.trimToSize();
-        return new String(bytes.elements());
-    }
-
-    /**
-     * Note. This is a STATIC method!
-     *
-     * @param in stream to read UTF chars from (endian irrelevant)
-     * @return string from stream
-     * @throws IOException if read fails.
-     */
-    public static String readUTF(DataInput in) throws IOException {
-        return DataInputStream.readUTF(in);
-    }
-
-    // -------------------------- PUBLIC INSTANCE  METHODS --------------------------
-    /**
-     * constructor.
-     *
-     * @param in binary inputstream of little-endian data.
-     */
-    public LEDataInputStream(InputStream in) {
-        this.is = in;
-        this.dis = new DataInputStream(in);
-        work = new byte[8];
-    }
-
-    /**
-     * close.
-     *
-     * @throws IOException if close fails.
-     */
-    public final void close() throws IOException {
-        dis.close();
-    }
-
-    /**
-     * Read bytes. Watch out, read may return fewer bytes than requested.
-     *
-     * @param ba  where the bytes go.
-     * @param off offset in buffer, not offset in file.
-     * @param len count of bytes to read.
-     * @return how many bytes read.
-     * @throws IOException if read fails.
-     */
-    public final int read(byte ba[], int off, int len) throws IOException {
-        // For efficiency, we avoid one layer of wrapper
-        return is.read(ba, off, len);
-    }
-
-    /**
-     * read only a one-byte boolean.
-     *
-     * @return true or false.
-     * @throws IOException if read fails.
-     * @see java.io.DataInput#readBoolean()
-     */
-    public final boolean readBoolean() throws IOException {
-        return dis.readBoolean();
-    }
-
-    /**
-     * read byte.
-     *
-     * @return the byte read.
-     * @throws IOException if read fails.
-     * @see java.io.DataInput#readByte()
-     */
-    public final byte readByte() throws IOException {
-        return dis.readByte();
-    }
-
-    /**
-     * Read on char. like DataInputStream.readChar except little endian.
-     *
-     * @return little endian 16-bit unicode char from the stream.
-     * @throws IOException if read fails.
-     */
-    public final char readChar() throws IOException {
-        dis.readFully(work, 0, 2);
-        return (char) ((work[1] & 0xff) << 8 | (work[0] & 0xff));
-    }
-
-    /**
-     * Read a double. like DataInputStream.readDouble except little endian.
-     *
-     * @return little endian IEEE double from the datastream.
-     * @throws IOException
-     */
-    public final double readDouble() throws IOException {
-        return Double.longBitsToDouble(readLong());
-    }
-
-    /**
-     * Read one float. Like DataInputStream.readFloat except little endian.
-     *
-     * @return little endian IEEE float from the datastream.
-     * @throws IOException if read fails.
-     */
-    public final float readFloat() throws IOException {
-        return Float.intBitsToFloat(readInt());
-    }
-
-    /**
-     * Read bytes until the array is filled.
-     *
-     * @see java.io.DataInput#readFully(byte[])
-     */
-    public final void readFully(byte ba[]) throws IOException {
-        dis.readFully(ba, 0, ba.length);
-    }
-
-    /**
-     * Read bytes until the count is satisfied.
-     *
-     * @throws IOException if read fails.
-     * @see java.io.DataInput#readFully(byte[],int,int)
-     */
-    public final void readFully(byte ba[],
-                                int off,
-                                int len) throws IOException {
-        dis.readFully(ba, off, len);
-    }
-
-    /**
-     * Read an int, 32-bits. Like DataInputStream.readInt except little endian.
-     *
-     * @return little-endian binary int from the datastream
-     * @throws IOException if read fails.
-     */
-    public final int readInt() throws IOException {
-        dis.readFully(work, 0, 4);
-        return (work[3]) << 24 | (work[2] & 0xff) << 16 | (work[1] & 0xff) << 8 | (work[0] & 0xff);
-    }
-
-    /**
-     * Read a line.
-     *
-     * @return a rough approximation of the 8-bit stream as a 16-bit unicode string
-     * @throws IOException
-     * @noinspection deprecation
-     * @deprecated This method does not properly convert bytes to characters. Use a Reader instead with a little-endian
-     *             encoding.
-     */
-    public final String readLine() throws IOException {
-        return dis.readLine();
-    }
-
-    /**
-     * read a long, 64-bits.  Like DataInputStream.readLong except little endian.
-     *
-     * @return little-endian binary long from the datastream.
-     * @throws IOException
-     */
-    public final long readLong() throws IOException {
-        dis.readFully(work, 0, 8);
-        return (long) (work[7]) << 56 |
-                (long) (work[6] & 0xff) << 48 |
-                (long) (work[5] & 0xff) << 40 |
-                (long) (work[4] & 0xff) << 32 |
-                (long) (work[3] & 0xff) << 24 |
-                (long) (work[2] & 0xff) << 16 |
-                (long) (work[1] & 0xff) << 8 |
-                (long) (work[0] & 0xff);
-    }
-
-    /**
-     * Read short, 16-bits. Like DataInputStream.readShort except little endian.
-     *
-     * @return little endian binary short from stream.
-     * @throws IOException if read fails.
-     */
-    public final short readShort() throws IOException {
-        dis.readFully(work, 0, 2);
-        return (short) ((work[1] & 0xff) << 8 | (work[0] & 0xff));
-    }
-
-    /**
-     * Read UTF counted string.
-     *
-     * @return String read.
-     */
-    public final String readUTF() throws IOException {
-        return dis.readUTF();
-    }
-
-    /**
-     * Read an unsigned byte. Note: returns an int, even though says Byte (non-Javadoc)
-     *
-     * @throws IOException if read fails.
-     * @see java.io.DataInput#readUnsignedByte()
-     */
-    public final int readUnsignedByte() throws IOException {
-        return dis.readUnsignedByte();
-    }
-
-    /**
-     * Read an unsigned short, 16 bits. Like DataInputStream.readUnsignedShort except little endian. Note, returns int
-     * even though it reads a short.
-     *
-     * @return little-endian int from the stream.
-     * @throws IOException if read fails.
-     */
-    public final int readUnsignedShort() throws IOException {
-        dis.readFully(work, 0, 2);
-        return ((work[1] & 0xff) << 8 | (work[0] & 0xff));
-    }
-
-    /**
-     * Skip over bytes in the stream. See the general contract of the <code>skipBytes</code> method of
-     * <code>DataInput</code>.
-     * <p/>
-     * Bytes for this operation are read from the contained input stream.
-     *
-     * @param n the number of bytes to be skipped.
-     * @return the actual number of bytes skipped.
-     * @throws IOException if an I/O error occurs.
-     */
-    public final int skipBytes(int n) throws IOException {
-        return dis.skipBytes(n);
-    }
-}
diff --git a/src/org/broad/igv/tdf/LinearIndex.java b/src/org/broad/igv/tdf/LinearIndex.java
index 3e480b6..8cc471a 100644
--- a/src/org/broad/igv/tdf/LinearIndex.java
+++ b/src/org/broad/igv/tdf/LinearIndex.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/tdf/ReadHeader.java b/src/org/broad/igv/tdf/ReadHeader.java
deleted file mode 100644
index 26dd457..0000000
--- a/src/org/broad/igv/tdf/ReadHeader.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.tdf;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-
-/**
- * Test class demonstrating reading header of .tdf file
- *
- * @author jrobinso
- */
-public class ReadHeader {
-
-    public static void main(String[] args) throws IOException {
-
-        String url = "http://www.broadinstitute.org/igvdata/omega.12mer.tdf";
-
-        InputStream is = null;
-
-        try {
-            is = (new URL(url)).openStream();
-
-            LEDataInputStream dis = new LEDataInputStream(is);
-
-            int magicNumber = dis.readInt();
-            int version = dis.readInt();
-            long indexOffset = dis.readLong();
-            int indexSize = dis.readInt();
-
-            int nHeaderBytes = dis.readInt();
-            String trackLine = dis.readString();
-            int numberOfTracks = dis.readInt();
-            String firstTrackName = dis.readString();
-
-            System.out.println("Magic number = " + magicNumber);
-            System.out.println("Version = " + version);
-            System.out.println("Index offset = " + indexOffset);
-            System.out.println("Index size = " + indexSize);
-            System.out.println("# header bytes = " + nHeaderBytes);
-            System.out.println("Track line = " + trackLine);
-            System.out.println("# of tracks = " + numberOfTracks);
-            System.out.println("First track name =" + firstTrackName);
-
-
-        } finally {
-            if (is != null) {
-                is.close();
-            }
-        }
-
-        /***** OUTPUT ********
-         Magic number = 826688073
-         Version = 1
-         Index offset = 20660918880
-         Index size = 4529
-         # header bytes = 1050
-         Track line = OTHER
-         # of tracks = 1667330676
-         First track name =k coords=0 name=omega_12 type=wiggle_0 color=80,40,0 visibility=2 description=omega_in_12mer_windows altColor=80,0,0 maxHeightPixels=50:40:30 smoothingWindow=
-         */
-
-    }
-}
diff --git a/src/org/broad/igv/tdf/TDFBedTile.java b/src/org/broad/igv/tdf/TDFBedTile.java
index 3cd5a89..9617c24 100644
--- a/src/org/broad/igv/tdf/TDFBedTile.java
+++ b/src/org/broad/igv/tdf/TDFBedTile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,12 +22,15 @@
  */
 package org.broad.igv.tdf;
 
+import org.broad.igv.util.StringUtils;
+
 import java.io.IOException;
+import java.nio.ByteBuffer;
 
 /**
  * @author jrobinso
  */
-public class TDFBedTile implements IBFTile {
+public class TDFBedTile implements TDFTile {
 
     int tileStart;
     int[] start;
@@ -35,8 +38,8 @@ public class TDFBedTile implements IBFTile {
     float[][] data;
     String[] name;
 
-    public TDFBedTile(LEDataInputStream inputStream, int nSamples, IBFTile.Type type) throws IOException {
-        this.fill(inputStream, nSamples, type);
+    public TDFBedTile(ByteBuffer byteBuffer, int nSamples, TDFTile.Type type) throws IOException {
+        this.fill(byteBuffer, nSamples, type);
     }
 
     public TDFBedTile(int tileStart, int[] start, int[] end, float[][] data) {
@@ -79,70 +82,72 @@ public class TDFBedTile implements IBFTile {
         return data[row][idx];
     }
 
-    public void writeTo(LEBufferedDataOutputStream fos) throws IOException {
+    public void writeTo(BufferedByteWriter fos) throws IOException {
 
         // File type
-        IBFTile.Type type = name == null ? IBFTile.Type.bed : IBFTile.Type.bedWithName;
-        fos.writeString(type.toString());
+        TDFTile.Type type = name == null ? TDFTile.Type.bed : TDFTile.Type.bedWithName;
+        fos.putNullTerminatedString(type.toString());
 
         int nPositions = start.length;
         int nSamples = data.length;
 
 
-        fos.writeInt(nPositions);
+        fos.putInt(nPositions);
 
         for (int i = 0; i < nPositions; i++) {
-            fos.writeInt(start[i]);
+            fos.putInt(start[i]);
         }
         for (int i = 0; i < nPositions; i++) {
-            fos.writeInt(end[i]);
+            fos.putInt(end[i]);
         }
 
-        fos.writeInt(nSamples);
+        fos.putInt(nSamples);
         for (int i = 0; i < data.length; i++) {
             for (int j = 0; j < data[i].length; j++) {
-                fos.writeFloat(data[i][j]);
+                fos.putFloat(data[i][j]);
             }
         }
 
         // Optionally record feature names
-        if (type == IBFTile.Type.bedWithName) {
+        if (type == TDFTile.Type.bedWithName) {
             for (int i = 0; i < nPositions; i++) {
-                fos.writeString(name[i]);
+                fos.putNullTerminatedString(name[i]);
             }
         }
 
 
     }
 
-    private void fill(LEDataInputStream dis, int nSamples, IBFTile.Type type) throws IOException {
+    private void fill(ByteBuffer byteBuffer, int nSamples, TDFTile.Type type) throws IOException {
 
-        int nPositions = dis.readInt();
+        int nPositions = byteBuffer.getInt();
         start = new int[nPositions];
         for (int i = 0; i < nPositions; i++) {
-            start[i] = dis.readInt();
+            start[i] = byteBuffer.getInt();
         }
         end = new int[nPositions];
         for (int i = 0; i < nPositions; i++) {
-            end[i] = dis.readInt();
+            end[i] = byteBuffer.getInt();
+            ;
         }
 
-        int nS = dis.readInt();
+        int nS = byteBuffer.getInt();
+        ;
         assert (nS == nSamples);
 
         data = new float[nS][nPositions];
         for (int row = 0; row < nS; row++) {
             data[row] = new float[nPositions];
             for (int i = 0; i < nPositions; i++) {
-                data[row][i] = dis.readFloat();
+                data[row][i] = byteBuffer.getFloat();
             }
         }
 
         // Optionally read feature names
-        if (type == IBFTile.Type.bedWithName) {
+        if (type == TDFTile.Type.bedWithName) {
             name = new String[nPositions];
             for (int i = 0; i < nPositions; i++) {
-                name[i] = dis.readString();
+                name[i] = StringUtils.readString(byteBuffer);
             }
 
         }
diff --git a/src/org/broad/igv/tdf/TDFDataSource.java b/src/org/broad/igv/tdf/TDFDataSource.java
index 56bf3a8..cb74627 100644
--- a/src/org/broad/igv/tdf/TDFDataSource.java
+++ b/src/org/broad/igv/tdf/TDFDataSource.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,15 +23,16 @@
 package org.broad.igv.tdf;
 
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
+import org.broad.igv.Globals;
+import org.broad.igv.PreferenceManager;
 import org.broad.igv.data.BasicScore;
 import org.broad.igv.data.DataSource;
 import org.broad.igv.feature.Chromosome;
 import org.broad.igv.feature.Genome;
 import org.broad.igv.feature.LocusScore;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.track.TrackType;
 import org.broad.igv.track.WindowFunction;
-import org.broad.igv.ui.IGVModel;
 import org.broad.igv.util.LRUCache;
 
 import java.util.ArrayList;
@@ -50,7 +51,7 @@ public class TDFDataSource implements DataSource {
     TDFReader reader;
     private int trackNumber = 0;
     String trackName;
-    LRUCache<String, List<LocusScore>> summaryScoreCache = new LRUCache(20);
+    LRUCache<String, List<LocusScore>> summaryScoreCache = new LRUCache(this, 10);
     Genome genome;
     Interval currentInterval;
     WindowFunction windowFunction = WindowFunction.mean;
@@ -58,15 +59,20 @@ public class TDFDataSource implements DataSource {
 
     private boolean aggregateLikeBins = true;
 
+    float normalizationFactor = 1.0f;
+
+
     public TDFDataSource(TDFReader reader, int trackNumber, String trackName) {
 
+        boolean normalizeCounts = PreferenceManager.getInstance().getBooleanPreference(PreferenceManager.NORMALIZE_COVERAGE, false);
+
         // TODO -- a single reader will be shared across data sources
         this.trackNumber = trackNumber;
         this.trackName = trackName;
         this.reader = reader;
         this.availableFunctions = reader.getWindowFunctions();
 
-        IBFGroup rootGroup = reader.getGroup("/");
+        TDFGroup rootGroup = reader.getGroup("/");
         try {
             maxZoom = Integer.parseInt(rootGroup.getAttribute("maxZoom"));
         } catch (Exception e) {
@@ -75,11 +81,22 @@ public class TDFDataSource implements DataSource {
         try {
             String dataGenome = rootGroup.getAttribute("genome");
             // TODO -- throw exception if data genome != current genome
-            genome = IGVModel.getInstance().getViewContext().getGenome();
+            genome = ViewContext.getInstance().getGenome();
         } catch (Exception e) {
             log.error("Unknown genome " + rootGroup.getAttribute("genome"));
             throw new RuntimeException("Unknown genome " + rootGroup.getAttribute("genome"));
         }
+        try {
+            if (normalizeCounts) {
+                String totalCountString = rootGroup.getAttribute("totalCount");
+                if (totalCountString != null) {
+                    int totalCount = Integer.parseInt(totalCountString);
+                    normalizationFactor = 1.0e6f / totalCount;
+                }
+            }
+        } catch (Exception e) {
+            log.error("Error reading attribute 'maxZoom'", e);
+        }
 
     }
 
@@ -91,12 +108,12 @@ public class TDFDataSource implements DataSource {
         return trackName;
     }
 
-    public double getDataMax(String chr) {
-        return reader.getUpperLimit();
+    public double getDataMax() {
+        return reader.getUpperLimit() * normalizationFactor;
     }
 
-    public double getDataMin(String chr) {
-        return reader.getLowerLimit();
+    public double getDataMin() {
+        return reader.getLowerLimit() * normalizationFactor;
     }
 
     public void setAggregateLikeBins(boolean aggregateLikeBins) {
@@ -157,16 +174,17 @@ public class TDFDataSource implements DataSource {
 
             if (zoom <= this.maxZoom) {
                 scores = new ArrayList(1000);
-                IBFDataset ds = reader.getDataset(chr, zoom, windowFunction);
+                TDFDataset ds = reader.getDataset(chr, zoom, windowFunction);
                 if (ds != null) {
-                    List<IBFTile> tiles = ds.getTiles(startLocation, endLocation);
+                    List<TDFTile> tiles = ds.getTiles(startLocation, endLocation);
                     if (tiles.size() > 0) {
-                        for (IBFTile tile : tiles) {
+                        for (TDFTile tile : tiles) {
 
                             if (tile.getSize() > 0) {
                                 for (int i = 0; i < tile.getSize(); i++) {
                                     float v = tile.getValue(trackNumber, i);
                                     if (!Float.isNaN(v)) {
+                                        v *= normalizationFactor;
                                         scores.add(new BasicScore(chr, tile.getStartPosition(i), tile.getEndPosition(i), v));
                                     }
                                 }
@@ -191,7 +209,7 @@ public class TDFDataSource implements DataSource {
 
         String dsName = "/" + chr + "/raw";
 
-        IBFDataset rawDataset = reader.getDataset(dsName);
+        TDFDataset rawDataset = reader.getDataset(dsName);
         if (rawDataset != null) {
 
             // The minimum bins size is 1 bp
@@ -199,10 +217,9 @@ public class TDFDataSource implements DataSource {
             double binSize = ((float) (endLocation - startLocation)) / nBins;
             Bin[] bins = new Bin[nBins];
 
-            List<IBFTile> rawTiles = rawDataset.getTiles(startLocation, endLocation);
+            List<TDFTile> rawTiles = rawDataset.getTiles(startLocation, endLocation);
             if (rawTiles.size() > 0) {
-
-                for (IBFTile rawTile : rawTiles) {
+                for (TDFTile rawTile : rawTiles) {
                     // Tile of raw data
                     if (rawTile != null && rawTile.getSize() > 0) {
 
@@ -219,7 +236,7 @@ public class TDFDataSource implements DataSource {
                             String probeName = rawTile.getName(i);
                             float v = rawTile.getValue(trackNumber, i);
                             if (!Float.isNaN(v)) {
-
+                                v *= normalizationFactor;
                                 int startBin = (int) Math.max(0, ((s - startLocation) / binSize));
                                 int endBin = (int) Math.min(bins.length - 1, ((e - startLocation) / binSize));
                                 for (int b = startBin; b <= endBin; b++) {
@@ -249,7 +266,7 @@ public class TDFDataSource implements DataSource {
                         currentBin = bins[b];
                     } else {
                         if (aggregateLikeBins && currentBin.isExtension(bins[b])) {
-                            currentBin.end = bins[b].end;
+                            currentBin.setEnd(bins[b].getEnd());
                         } else {
                             scores.add(currentBin);
                             currentBin = bins[b];
@@ -267,7 +284,7 @@ public class TDFDataSource implements DataSource {
 
     /*
      *
-    Chromosome chromosome = IGVModel.getInstance().getViewContext().getGenome().getChromosome(chr);
+    Chromosome chromosome = ViewContext.getInstance().getGenome().getChr(chr);
     if(chromosome == null) {
     return null;
     }
@@ -282,7 +299,7 @@ public class TDFDataSource implements DataSource {
         // TODO -- this whole section could be computed once and stored,  it is only a function
         // of the genome, chr, and zoom level.
         double tileWidth = 0;
-        if (chr.equals(IGVConstants.CHR_ALL)) {
+        if (chr.equals(Globals.CHR_ALL)) {
             tileWidth = (genome.getLength() / 1000.0);
         } else {
             if (chromosome != null) {
@@ -312,19 +329,6 @@ public class TDFDataSource implements DataSource {
         return scores;
     }
 
-    // IBFTile computeTile(IBFDataset ds, int t, List<LocusScore> scores, String chr)
-    synchronized IBFTile getTile(IBFDataset ds, int t, String chr, Map<String, IBFTile> cache) {
-        String key = ds.getName() + "_" + t;
-
-        IBFTile tile = null;
-        if (!cache.containsKey(key)) {
-            tile = reader.readTile(ds, t);
-            cache.put(key, tile);
-        } else {
-            tile = cache.get(key);
-        }
-        return tile;
-    }
 
     public TrackType getTrackType() {
         return reader.getTrackType();
@@ -336,7 +340,7 @@ public class TDFDataSource implements DataSource {
 
     public boolean isLogNormalized() {
         //return false;
-        return getDataMin(null) < 0;
+        return getDataMin() < 0;
     }
 
     public void refreshData(long timestamp) {
diff --git a/src/org/broad/igv/tdf/TDFDataset.java b/src/org/broad/igv/tdf/TDFDataset.java
new file mode 100644
index 0000000..cb9cbca
--- /dev/null
+++ b/src/org/broad/igv/tdf/TDFDataset.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.tdf;
+
+import org.broad.igv.util.LRUCache;
+import org.broad.igv.util.StringUtils;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Represents the data for a particular chromosome and zoom level
+ *
+ * @author jrobinso
+ */
+public class TDFDataset extends TDFEntity {
+
+    public enum DataType {
+
+        BYTE, SHORT, INT, FLOAT, DOUBLE, STRING
+    }
+
+    ;
+    DataType dataType;
+    int tileWidth;
+    long[] tilePositions;
+    int[] tileSizes;
+    int nTiles;
+    LRUCache<String, TDFTile> cache = new LRUCache(this, 10);
+    // TODO -- refactor this dependency out
+    TDFReader reader;
+
+    public TDFDataset(String name, DataType dataType, int tileWidth, int nTiles) {
+        super(name);
+        this.dataType = dataType;
+        this.tileWidth = tileWidth;
+        this.nTiles = nTiles;
+        this.tilePositions = new long[nTiles];
+        this.tileSizes = new int[nTiles];
+
+        // Initialize tile positions to -1.  This indicates a blank tile and is the default
+        Arrays.fill(tilePositions, -1);
+
+    }
+
+    public TDFDataset(String name, ByteBuffer byteBuffer, TDFReader reader) throws IOException {
+        super(name);
+        this.reader = reader;
+        fill(byteBuffer);
+    }
+
+    public void write(BufferedByteWriter dos) throws IOException {
+
+        writeAttributes(dos);
+
+        writeString(dos, dataType.toString());
+        dos.putFloat(tileWidth);
+        //       dos.writeFloat(binWidth);
+        dos.putInt(tilePositions.length);
+        for (int i = 0; i < tilePositions.length; i++) {
+            dos.putLong(tilePositions[i]);
+            dos.putInt(tileSizes[i]);
+        }
+
+    }
+
+    private void fill(ByteBuffer byteBuffer) throws IOException {
+
+        // Attributes
+        readAttributes(byteBuffer);
+
+        String typeString = StringUtils.readString(byteBuffer);
+        dataType = TDFDataset.DataType.valueOf(typeString);
+
+        // TODO -- change tileWidth to int ?
+        tileWidth = (int) byteBuffer.getFloat();
+
+
+        nTiles = byteBuffer.getInt();
+        tilePositions = new long[nTiles];
+        tileSizes = new int[nTiles];
+
+        for (int i = 0; i < nTiles; i++) {
+            tilePositions[i] = byteBuffer.getLong();
+            tileSizes[i] = byteBuffer.getInt();
+        }
+
+    }
+
+    // TODO -- this uses an implied linear index.  Abstract index or replace
+    // with general interval index
+    // TODO -- gather all non-cached tiles and read in one chunk.  See BAM
+    // alignment reader class
+    public List<TDFTile> getTiles(int startLocation, int endLocation) {
+
+        List<TDFTile> tiles = new ArrayList();
+        int startTile = (int) (startLocation / tileWidth);
+        int endTile = (int) (endLocation / tileWidth);
+        for (int t = startTile; t <= endTile; t++) {
+            TDFTile tile = getTile(t);
+            if (tile != null && tile.getSize() > 0) {
+                tiles.add(tile);
+            }
+        }
+        return tiles;
+
+    }
+
+    // TDFTile computeTile(TDFDataset ds, int t, List<LocusScore> scores, String chr)
+    synchronized TDFTile getTile(int t) {
+        String key = getName() + "_" + t;
+
+        TDFTile tile = null;
+        if (!cache.containsKey(key)) {
+            tile = reader.readTile(this, t);
+            cache.put(key, tile);
+        } else {
+            tile = cache.get(key);
+        }
+        return tile;
+    }
+
+}
diff --git a/src/org/broad/igv/tdf/TDFEntity.java b/src/org/broad/igv/tdf/TDFEntity.java
new file mode 100644
index 0000000..f6a6258
--- /dev/null
+++ b/src/org/broad/igv/tdf/TDFEntity.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.tdf;
+
+import org.broad.igv.util.StringUtils;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author jrobinso
+ */
+public class TDFEntity {
+
+    Map<String, String> attributes;
+
+    private String name;
+
+    public TDFEntity(String name) {
+        this.name = name;
+        this.attributes = new HashMap();
+    }
+
+    public TDFEntity(String name, Map<String, String> attributes) {
+        this.name = name;
+        this.attributes = attributes;
+    }
+
+    public Set<String> getAttributeNames() {
+        return attributes.keySet();
+    }
+
+    public String getAttribute(String name) {
+        return attributes.get(name);
+    }
+
+    public void setAttribute(String name, String value) {
+        attributes.put(name, value);
+    }
+
+    void writeAttributes(BufferedByteWriter dos) throws IOException {
+        dos.putInt(attributes.size());
+        for (Map.Entry<String, String> entry : attributes.entrySet()) {
+            writeString(dos, entry.getKey());
+            writeString(dos, entry.getValue());
+        }
+    }
+
+    void readAttributes(ByteBuffer byteBuffer) throws IOException {
+        int nAttributes = byteBuffer.getInt();
+        while (nAttributes-- > 0) {
+            String key = StringUtils.readString(byteBuffer);
+            String value = StringUtils.readString(byteBuffer);
+            setAttribute(key, value);
+        }
+    }
+
+    /**
+     * Write a string as a null terminated character sequence.
+     * <p/>
+     * IGV requires all strings to be ascii,  so single byte storaged is enough
+     *
+     * @param dos
+     * @param s
+     * @throws java.io.IOException
+     */
+    public void writeString(BufferedByteWriter dos, String s) throws IOException {
+        dos.putNullTerminatedString(s);
+    }
+
+    /**
+     * @return the name
+     */
+    public String getName() {
+        return name;
+    }
+}
diff --git a/src/org/broad/igv/tdf/TDFFixedTile.java b/src/org/broad/igv/tdf/TDFFixedTile.java
new file mode 100644
index 0000000..ebd56ab
--- /dev/null
+++ b/src/org/broad/igv/tdf/TDFFixedTile.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.tdf;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * @author jrobinso
+ */
+public class TDFFixedTile implements TDFTile {
+
+    int tileStart;
+    float span;
+    int start;
+    float[][] data;
+
+    public TDFFixedTile(ByteBuffer byteBuffer, int nSamples) throws IOException {
+        this.fill(byteBuffer, nSamples);
+    }
+
+    public TDFFixedTile(int tileStart, int start, float span, float[][] data) {
+        this.tileStart = tileStart;
+        this.span = span;
+        this.data = data;
+        this.start = start;
+    }
+
+    public int getTileStart() {
+        return start;
+    }
+
+    public int getTileEnd() {
+        return getSize() == 0 ? 0 : getEndPosition(getSize() - 1);
+    }
+
+    public int getStartPosition(int idx) {
+        return start + (int) (idx * span);
+    }
+
+    public int getEndPosition(int idx) {
+        return start + (int) ((idx + 1) * span);
+    }
+
+    public String getName(int idx) {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public float getValue(int row, int idx) {
+        return data[row][idx];
+    }
+
+    public int getSize() {
+        return (data == null ? 0 : data[0].length);
+    }
+
+
+    // TODO -- record "type",  extent (longest feature), other stuff
+
+    public void writeTo(BufferedByteWriter fos) throws IOException {
+
+        fos.putNullTerminatedString(TDFTile.Type.fixedStep.toString());
+        fos.putInt(getSize());
+        fos.putInt(start);
+        fos.putFloat(span);
+        for (int i = 0; i < data.length; i++) {
+            for (int j = 0; j < data[i].length; j++) {
+                fos.putFloat(data[i][j]);
+            }
+        }
+
+    }
+
+    public void fill(ByteBuffer byteBuffer, int nSamples) throws IOException {
+
+        int nPositions = byteBuffer.getInt();
+        start = byteBuffer.getInt();
+        span = byteBuffer.getFloat();
+
+        data = new float[nSamples][nPositions];
+        for (int sample = 0; sample < nSamples; sample++) {
+            data[sample] = new float[nPositions];
+            for (int i = 0; i < nPositions; i++) {
+                data[sample][i] = byteBuffer.getFloat();
+            }
+        }
+
+    }
+}
diff --git a/src/org/broad/igv/tdf/TDFGroup.java b/src/org/broad/igv/tdf/TDFGroup.java
new file mode 100644
index 0000000..4349a42
--- /dev/null
+++ b/src/org/broad/igv/tdf/TDFGroup.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.tdf;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+/**
+ * @author jrobinso
+ */
+public class TDFGroup extends TDFEntity {
+
+    public static final String USE_PERCENTILE_AUTOSCALING = "userPercentileAutoscaling";
+
+
+    TDFGroup(String name) {
+        super(name);
+    }
+
+    TDFGroup(String name, Map<String, String> attributes) {
+        super(name, attributes);
+    }
+
+    public TDFGroup(String name, ByteBuffer byteBuffer) throws IOException {
+        super(name);
+        fill(byteBuffer);
+    }
+
+    void write(BufferedByteWriter dos) throws IOException {
+
+        writeAttributes(dos);
+    }
+
+    void fill(ByteBuffer byteBuffer) throws IOException {
+        readAttributes(byteBuffer);
+    }
+}
diff --git a/src/org/broad/igv/tdf/TDFReader.java b/src/org/broad/igv/tdf/TDFReader.java
index 847eeca..6f9c31b 100644
--- a/src/org/broad/igv/tdf/TDFReader.java
+++ b/src/org/broad/igv/tdf/TDFReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,13 +23,19 @@
 package org.broad.igv.tdf;
 
 import org.apache.log4j.Logger;
+import org.broad.igv.exceptions.DataLoadException;
 import org.broad.igv.track.TrackType;
 import org.broad.igv.track.WindowFunction;
 import org.broad.igv.util.*;
-import org.broad.igv.exceptions.DataLoadException;
+import org.broad.igv.util.SeekableStream;
+import org.broad.igv.util.SeekableStreamFactory;
 
 import java.io.ByteArrayInputStream;
+import java.io.EOFException;
 import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 import java.util.*;
 
 /**
@@ -43,19 +49,18 @@ public class TDFReader {
     // Cache to insure there is only 1 reader per file
     static Map<ResourceLocator, TDFReader> readerCache = new HashMap();
 
-    
-    private SeekableStream fis = null;
+
+    private SeekableStream seekableStream = null;
     private int version;
     private Map<String, IndexEntry> datasetIndex;
     private Map<String, IndexEntry> groupIndex;
     private TrackType trackType;
     private String trackLine;
     private String[] trackNames;
-    LRUCache<String, IBFGroup> groupCache = new LRUCache(20);
-    LRUCache<String, IBFDataset> datasetCache = new LRUCache(20);
-    double data98 = Double.NaN;
-    double dataMax = Double.NaN;
-    double dataMin = Double.NaN;
+    LRUCache<String, TDFGroup> groupCache = new LRUCache(this, 10);
+    LRUCache<String, TDFDataset> datasetCache = new LRUCache(this, 10);
+
+    Map<WindowFunction, Double> valueCache = new HashMap();
     private List<WindowFunction> windowFunctions;
     ResourceLocator locator;
 
@@ -80,19 +85,28 @@ public class TDFReader {
         //this.path = path;
         this.locator = locator;
         try {
-            fis = SeekableStreamFactory.getStreamFor(locator);
+            seekableStream = getStream(locator);
             readHeader();
 
         } catch (IOException ex) {
-            log.error("Error creating file: " + locator.getPath(), ex);
-            throw new RuntimeException("Error creating file: " + locator.getPath(), ex);
+            log.error("Error loading file: " + locator.getPath(), ex);
+            throw new DataLoadException("Error loading file: " + ex.toString(), locator.getPath());
+        }
+    }
+
+    private SeekableStream getStream(ResourceLocator locator) throws IOException {
+        if (locator.getServerURL() != null) {
+            return new SeekableServiceStream(locator);
+
+        } else {
+            return SeekableStreamFactory.getStreamFor(locator.getPath());
         }
 
     }
 
     public void close() {
         try {
-            fis.close();
+            seekableStream.close();
         } catch (IOException e) {
             log.error("Error closing reader for: " + getPath(), e);
         }
@@ -108,32 +122,35 @@ public class TDFReader {
         // byte count + header byte count  (4 + 4 + 8 + 4 + 4)
         //byte[] buffer = new byte[24];
         //readFully(buffer);
-        byte[] buffer = fis.readBytes(0, 24);
+        byte[] buffer = readBytes(0, 24);
+        ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
+        byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
 
-        LEDataInputStream bdis = new LEDataInputStream(new ByteArrayInputStream(buffer));
-        int magicNumber = bdis.readInt();
+        int magicNumber = byteBuffer.getInt();
 
         byte[] magicBytes = new byte[4];
         System.arraycopy(buffer, 0, magicBytes, 0, 4);
         String magicString = new String(magicBytes);
 
-        if(magicNumber != 826688596) {
+        if (!(magicString.startsWith("TDF") || !magicString.startsWith("IBF"))) {
             String msg = "Error reading header: bad magic number.";
-            //throw new DataLoadException(msg, locator.getPath());
+            //    throw new DataLoadException(msg, locator.getPath());
         }
-        version = bdis.readInt();
-        long idxPosition = bdis.readLong();
-        int idxByteCount = bdis.readInt();
-        int nHeaderBytes = bdis.readInt();
 
-        byte[] bytes = fis.readBytes(24, nHeaderBytes);
-        bdis = new LEDataInputStream(new ByteArrayInputStream(bytes));
+        version = byteBuffer.getInt();
+        long idxPosition = byteBuffer.getLong();
+        int idxByteCount = byteBuffer.getInt();
+        int nHeaderBytes = byteBuffer.getInt();
+
+        byte[] bytes = readBytes(24, nHeaderBytes);
+        byteBuffer = ByteBuffer.wrap(bytes);
+        byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
 
         if (version >= 2) {
-            int nWFs = bdis.readInt();
+            int nWFs = byteBuffer.getInt();
             this.windowFunctions = new ArrayList(nWFs);
             for (int i = 0; i < nWFs; i++) {
-                String wfName = bdis.readString();
+                String wfName = StringUtils.readString(byteBuffer);
                 try {
                     windowFunctions.add(WindowFunction.valueOf(wfName));
                 }
@@ -145,27 +162,32 @@ public class TDFReader {
             windowFunctions = Arrays.asList(WindowFunction.mean);
         }
 
+        // Track type
         try {
-            trackType = TrackType.valueOf(bdis.readString());
+            trackType = TrackType.valueOf(StringUtils.readString(byteBuffer));
 
         } catch (Exception e) {
             trackType = TrackType.OTHER;
         }
-        trackLine = bdis.readString().trim();
-        int nTracks = bdis.readInt();
+
+        // Track line
+        trackLine = StringUtils.readString(byteBuffer).trim();
+
+
+        int nTracks = byteBuffer.getInt();
         trackNames = new String[nTracks];
         for (int i = 0; i < nTracks; i++) {
-            trackNames[i] = bdis.readString();
+            trackNames[i] = StringUtils.readString(byteBuffer);
         }
 
         // Flags
-        if(version >= 3) {
-            String genomeId = bdis.readString();
-
-            int flags = bdis.readInt();
+        if (version > 2) {
+            String genomeId = StringUtils.readString(byteBuffer);
+            int flags = byteBuffer.getInt();
             compressed = (flags & GZIP_FLAG) != 0;
         }
 
+
         readMasterIndex(idxPosition, idxByteCount);
 
     }
@@ -176,44 +198,44 @@ public class TDFReader {
         //fis.seek(idxPosition);
         //byte[] bytes = new byte[nBytes];
         //readFully(bytes);
-        byte[] bytes = fis.readBytes(idxPosition, nBytes);
+        byte[] bytes = readBytes(idxPosition, nBytes);
+        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
+        byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
 
-        LEDataInputStream bdis = new LEDataInputStream(new ByteArrayInputStream(bytes));
-        int nDatasets = bdis.readInt();
+        int nDatasets = byteBuffer.getInt();
 
         datasetIndex = new LinkedHashMap(nDatasets);
         for (int i = 0; i < nDatasets; i++) {
-            String name = bdis.readString();
-            long fPosition = bdis.readLong();
-            int n = bdis.readInt();
+            String name = StringUtils.readString(byteBuffer);
+            long fPosition = byteBuffer.getLong();
+            int n = byteBuffer.getInt();
             datasetIndex.put(name, new IndexEntry(fPosition, n));
         }
 
-        int nGroups = bdis.readInt();
+        int nGroups = byteBuffer.getInt();
         groupIndex = new LinkedHashMap(nGroups);
         for (int i = 0; i < nGroups; i++) {
-            String name = bdis.readString();
-            long fPosition = bdis.readLong();
-            int n = bdis.readInt();
+            String name = StringUtils.readString(byteBuffer);
+            long fPosition = byteBuffer.getLong();
+            int n = byteBuffer.getInt();
             groupIndex.put(name, new IndexEntry(fPosition, n));
         }
     }
 
-    public IBFDataset getDataset(String chr, int zoom, WindowFunction windowFunction) {
-        // TODO -- put here to handle 1 vs chr1 problems.
+    public TDFDataset getDataset(String chr, int zoom, WindowFunction windowFunction) {
 
         // Version 1 only had mean
         String wf = getVersion() < 2 ? "" : "/" + windowFunction.toString();
 
         String dsName = "/" + chr + "/z" + zoom + wf;
 
-        IBFDataset ds = getDataset(dsName);
+        TDFDataset ds = getDataset(dsName);
 
 
         return ds;
     }
 
-    public synchronized IBFDataset getDataset(String name) {
+    public synchronized TDFDataset getDataset(String name) {
 
         if (datasetCache.containsKey(name)) {
             return datasetCache.get(name);
@@ -228,12 +250,11 @@ public class TDFReader {
                 //fis.seek(position);
                 //byte[] buffer = new byte[nBytes];
                 //readFully(buffer);
-                byte[] buffer = fis.readBytes(position, nBytes);
+                byte[] buffer = readBytes(position, nBytes);
+                ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
+                byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
 
-                ByteArrayInputStream byteStream = new ByteArrayInputStream(buffer);
-                LEDataInputStream dis = new LEDataInputStream(byteStream);
-
-                IBFDataset ds = new IBFDataset(name, dis, this);
+                TDFDataset ds = new TDFDataset(name, byteBuffer, this);
                 datasetCache.put(name, ds);
                 return ds;
             } else {
@@ -254,7 +275,7 @@ public class TDFReader {
         return groupIndex.keySet();
     }
 
-    public synchronized IBFGroup getGroup(String name) {
+    public synchronized TDFGroup getGroup(String name) {
         if (groupCache.containsKey(name)) {
             return groupCache.get(name);
         }
@@ -267,11 +288,11 @@ public class TDFReader {
             //fis.seek(position);
             //byte[] buffer = new byte[nBytes];
             //readFully(buffer);
-            byte[] buffer = fis.readBytes(position, nBytes);
-
-            LEDataInputStream dis = new LEDataInputStream(new ByteArrayInputStream(buffer));
+            byte[] buffer = readBytes(position, nBytes);
+            ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
+            byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
 
-            IBFGroup group = new IBFGroup(name, dis);
+            TDFGroup group = new TDFGroup(name, byteBuffer);
 
             groupCache.put(name, group);
 
@@ -284,7 +305,8 @@ public class TDFReader {
     }
 
     // TODO -- move to dataset class
-    public synchronized IBFTile readTile(IBFDataset ds, int tileNumber) {
+
+    public synchronized TDFTile readTile(TDFDataset ds, int tileNumber) {
 
         try {
             if (tileNumber >= ds.tilePositions.length) {
@@ -294,22 +316,21 @@ public class TDFReader {
 
             long position = ds.tilePositions[tileNumber];
             if (position < 0) {
+
                 // Indicates empty tile
                 // TODO -- return an empty tile?
                 return null;
             }
 
             int nBytes = ds.tileSizes[tileNumber];
-            //fis.seek(position);
-            //byte[] buffer = new byte[nBytes];
-            //readFully(buffer);
-            byte[] buffer = fis.readBytes(position, nBytes);
-            if(compressed) {
+
+            byte[] buffer = readBytes(position, nBytes);
+            if (compressed) {
                 buffer = CompressionUtils.decompress(buffer);
-                
+
             }
 
-            return TileFactory.createTile(buffer, trackNames.length, compressed);
+            return TileFactory.createTile(buffer, trackNames.length);
         } catch (IOException ex) {
             String tileName = ds.getName() + "[" + tileNumber + "]";
             log.error("Error reading data tile: " + tileName, ex);
@@ -345,48 +366,60 @@ public class TDFReader {
         return trackNames;
     }
 
-    public double getUpperLimit() {
-
-        if (Double.isNaN(data98)) {
-            IBFGroup rootGroup = getGroup("/");
-            String maxString = rootGroup.getAttribute(WindowFunction.percentile98.getDisplayName());
-            data98 = 100;
+    private Double getValue(WindowFunction wf) {
+        if (!valueCache.containsKey(wf)) {
+            TDFGroup rootGroup = getGroup("/");
+            String maxString = rootGroup.getAttribute(wf.getDisplayName());
             try {
-                data98 = Double.parseDouble(maxString);
+                valueCache.put(wf, Double.parseDouble(maxString));
             } catch (Exception e) {
+                log.info("Warning: value '" + wf.toString() + "' not found in tdf value " + getPath());
+                valueCache.put(wf, null);
             }
         }
-        return data98;
+        return valueCache.get(wf);
     }
 
-    public double getDataMax() {
-
-        if (Double.isNaN(dataMax)) {
-            IBFGroup rootGroup = getGroup("/");
-            String maxString = rootGroup.getAttribute(WindowFunction.max.getDisplayName());
-            dataMax = 100;
-            try {
-                dataMax = Double.parseDouble(maxString);
-            } catch (Exception e) {
-            }
+    /**
+     * Return the default upper value for the data range.  A check is made to see if both upper and lower limits
+     * are equal to zero, within a tolerance.  If they are the upper limit is arbitrarily set to "100".  This is
+     * protection against the pathological case that can occur with datasets consisting of largely zero values.
+     *
+     * @return
+     */
+    public double getUpperLimit() {
+        Double val = getValue(WindowFunction.percentile98);
+        double upperLimit = val == null ? getDataMax() : val;
+        if (upperLimit < 1.0e-30 && getLowerLimit() < 1.0e-30) {
+            upperLimit = 100;
         }
-        return dataMax;
+        return upperLimit;
     }
 
     public double getLowerLimit() {
+        Double val = getValue(WindowFunction.percentile2);
+        return val == null ? getDataMin() : val;
+    }
 
-        if (Double.isNaN(dataMin)) {
-            IBFGroup rootGroup = getGroup("/");
-            String maxString = rootGroup.getAttribute(WindowFunction.percentile2.getDisplayName());
-            dataMin = 0;
-            try {
-                dataMin = Double.parseDouble(maxString);
-            } catch (Exception e) {
-            }
-        }
-        return dataMin;
+    public double getDataMax() {
+        Double val = getValue(WindowFunction.max);
+        return val == null ? 100 : val;
     }
 
+    public double getDataMin() {
+        Double val = getValue(WindowFunction.min);
+        return val == null ? 0 : val;
+    }
+
+
+    public byte[] readBytes(long position, int nBytes) throws IOException {
+        seekableStream.seek(position);
+        byte[] buffer = new byte[nBytes];
+        seekableStream.readFully(buffer);
+        return buffer;
+    }
+
+
     /**
      * @return the windowFunctions
      */
diff --git a/src/org/broad/igv/tdf/TDFTile.java b/src/org/broad/igv/tdf/TDFTile.java
new file mode 100644
index 0000000..db6935d
--- /dev/null
+++ b/src/org/broad/igv/tdf/TDFTile.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.broad.igv.tdf;
+
+import java.io.IOException;
+
+/**
+ * @author jrobinso
+ */
+public interface TDFTile {
+
+    enum Type {
+        fixedStep, variableStep, bed, bedWithName
+    }
+
+    ;
+
+    public int getTileStart();
+
+    public int getSize();
+
+    public int getStartPosition(int idx);
+
+    public int getEndPosition(int idx);
+
+    public String getName(int idx);
+
+    public float getValue(int row, int idx);
+
+    public void writeTo(BufferedByteWriter fos) throws IOException;
+
+}
diff --git a/src/org/broad/igv/tdf/TDFUtils.java b/src/org/broad/igv/tdf/TDFUtils.java
index 9b488b8..eb72c64 100644
--- a/src/org/broad/igv/tdf/TDFUtils.java
+++ b/src/org/broad/igv/tdf/TDFUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -44,7 +44,7 @@ public class TDFUtils {
     public static void dumpRootAttributes(String ibfFile) {
         TDFReader reader = TDFReader.getReader(ibfFile);
         System.out.println("Track line = " + reader.getTrackLine());
-        IBFGroup group = reader.getGroup("/");
+        TDFGroup group = reader.getGroup("/");
         for (Map.Entry<String, String> entries : group.attributes.entrySet()) {
             System.out.println(entries.getKey() + " = " + entries.getValue());
         }
@@ -57,7 +57,7 @@ public class TDFUtils {
         System.out.println("DATASETS");
         for (String dsName : reader.getDatasetNames()) {
             System.out.println(dsName);
-            IBFDataset ds = reader.getDataset(dsName);
+            TDFDataset ds = reader.getDataset(dsName);
 
             System.out.println("Attributes");
             for (Map.Entry<String, String> entry : ds.attributes.entrySet()) {
@@ -79,10 +79,10 @@ public class TDFUtils {
         System.out.println("DATASETS");
         for (String dsName : reader.getDatasetNames()) {
             System.out.println(dsName);
-            IBFDataset ds = reader.getDataset(dsName);
+            TDFDataset ds = reader.getDataset(dsName);
 
             for (int i = 0; i < ds.nTiles; i++) {
-                IBFTile tile = ds.getTile(i);
+                TDFTile tile = ds.getTile(i);
                 if (tile != null) {
                     System.out.println("Tile: " + i);
                     dumpTileData(reader, tile);
@@ -94,8 +94,8 @@ public class TDFUtils {
     public static void dumpTile(String ibfFile, String dsName, int tileNumber) {
 
         TDFReader reader = TDFReader.getReader(ibfFile);
-        IBFDataset ds = reader.getDataset(dsName);
-        IBFTile tile = reader.readTile(ds, tileNumber);
+        TDFDataset ds = reader.getDataset(dsName);
+        TDFTile tile = reader.readTile(ds, tileNumber);
         if (tile == null) {
             System.out.println("Null tile: " + dsName + " [" + tileNumber + "]");
         } else {
@@ -103,7 +103,7 @@ public class TDFUtils {
         }
     }
 
-    private static void dumpTileData(TDFReader reader, IBFTile tile) {
+    private static void dumpTileData(TDFReader reader, TDFTile tile) {
         int nTracks = reader.getTrackNames().length;
         int nBins = tile.getSize();
         if (nBins > 0) {
@@ -121,14 +121,14 @@ public class TDFUtils {
 
     public static void dumpRange(String ibfFile, String dsName, int startLocation, int endLocation) {
         TDFReader reader = TDFReader.getReader(ibfFile);
-        IBFDataset ds = reader.getDataset(dsName);
+        TDFDataset ds = reader.getDataset(dsName);
 
         int tileWidth = ds.tileWidth;
         int startTile = startLocation / tileWidth;
         int endTile = endLocation / tileWidth;
 
         for (int tileNumber = startTile; tileNumber <= endTile; tileNumber++) {
-            IBFTile tile = reader.readTile(ds, tileNumber);
+            TDFTile tile = reader.readTile(ds, tileNumber);
             if (tile == null) {
                 System.out.println("Null tile: " + dsName + " [" + tileNumber + "]");
             } else {
@@ -188,7 +188,7 @@ public class TDFUtils {
         System.out.println("DATASETS");
         for (String dsName : reader.getDatasetNames()) {
             System.out.println(dsName);
-            IBFDataset ds = reader.getDataset(dsName);
+            TDFDataset ds = reader.getDataset(dsName);
 
             System.out.println("Attributes");
             for (Map.Entry<String, String> entry : ds.attributes.entrySet()) {
@@ -202,7 +202,7 @@ public class TDFUtils {
             int tracksToShow = Math.min(4, nTracks);
 
             for (int i = 0; i < ds.nTiles; i++) {
-                IBFTile tile = reader.readTile(ds, i);
+                TDFTile tile = reader.readTile(ds, i);
                 if (tile != null) {
                     System.out.print("  " + i);
                     /*int nBins = tile.getSize();
@@ -227,7 +227,7 @@ public class TDFUtils {
         System.out.println("GROUPS");
         for (String name : reader.getGroupNames()) {
             System.out.println(name);
-            IBFGroup group = reader.getGroup(name);
+            TDFGroup group = reader.getGroup(name);
 
             System.out.println("Attributes");
             for (Map.Entry<String, String> entry : group.attributes.entrySet()) {
diff --git a/src/org/broad/igv/tdf/TDFVaryTile.java b/src/org/broad/igv/tdf/TDFVaryTile.java
new file mode 100644
index 0000000..c654e66
--- /dev/null
+++ b/src/org/broad/igv/tdf/TDFVaryTile.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.tdf;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * @author jrobinso
+ */
+public class TDFVaryTile implements TDFTile {
+
+    int tileStart;
+    float span;
+    int[] start;
+    float[][] data;
+
+    public TDFVaryTile(ByteBuffer byteBuffer, int nSamples) throws IOException {
+        this.fill(byteBuffer, nSamples);
+    }
+
+    public TDFVaryTile(int tileStart, float span, int[] start, float[][] data) {
+        this.tileStart = tileStart;
+        this.span = span;
+        this.start = start;
+        this.data = data;
+    }
+
+
+    public int getSize() {
+        return start.length;
+    }
+
+    public int getTileStart() {
+        return tileStart;
+    }
+
+    public int getStartPosition(int idx) {
+        return start[idx];
+    }
+
+    public int getEndPosition(int idx) {
+        return (int) (start[idx] + span);
+    }
+
+    public String getName(int idx) {
+        return null;
+    }
+
+    public float getValue(int row, int idx) {
+        return data[row][idx];
+    }
+
+    public void writeTo(BufferedByteWriter fos) throws IOException {
+
+        // File type
+        fos.putNullTerminatedString(TDFTile.Type.variableStep.toString());
+
+        fos.putInt(tileStart);
+        fos.putFloat(span);
+
+        int nPositions = start.length;
+        int nSamples = data.length;
+
+        fos.putInt(nPositions);
+
+        for (int i = 0; i < nPositions; i++) {
+            fos.putInt(start[i]);
+        }
+
+        fos.putInt(nSamples);
+        for (int i = 0; i < data.length; i++) {
+            for (int j = 0; j < data[i].length; j++) {
+                fos.putFloat(data[i][j]);
+            }
+        }
+    }
+
+    public void fill(ByteBuffer byteBuffer, int nSamples) throws IOException {
+
+        tileStart = byteBuffer.getInt();
+        span = byteBuffer.getFloat();
+
+        int nPositions = byteBuffer.getInt();
+        start = new int[nPositions];
+        for (int i = 0; i < nPositions; i++) {
+            start[i] = byteBuffer.getInt();
+        }
+
+        int nS = byteBuffer.getInt();
+        assert (nS == nSamples);
+
+        data = new float[nS][nPositions];
+        for (int row = 0; row < nS; row++) {
+            data[row] = new float[nPositions];
+            for (int i = 0; i < nPositions; i++) {
+                data[row][i] = byteBuffer.getFloat();
+            }
+        }
+
+    }
+
+}
diff --git a/src/org/broad/igv/tdf/TDFWriter.java b/src/org/broad/igv/tdf/TDFWriter.java
index b343a95..ed777dd 100644
--- a/src/org/broad/igv/tdf/TDFWriter.java
+++ b/src/org/broad/igv/tdf/TDFWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,8 +22,8 @@
  */
 package org.broad.igv.tdf;
 
-import com.mindprod.ledatastream.LEDataOutputStream;
 import org.apache.log4j.Logger;
+import org.broad.igv.exceptions.DataLoadException;
 import org.broad.igv.track.TrackType;
 import org.broad.igv.track.WindowFunction;
 import org.broad.igv.util.CompressionUtils;
@@ -46,44 +46,42 @@ public class TDFWriter {
 
     static private Logger log = Logger.getLogger(TDFWriter.class);
     static private int version = 3;
-    LEBufferedDataOutputStream fos = null;
+    OutputStream fos = null;
+    int bytesWritten = 0;
+
     File file;
-    Map<String, IBFGroup> groupCache = new LinkedHashMap();
-    Map<String, IBFDataset> datasetCache = new LinkedHashMap();
+    Map<String, TDFGroup> groupCache = new LinkedHashMap();
+    Map<String, TDFDataset> datasetCache = new LinkedHashMap();
     Map<String, IndexEntry> datasetIndex = new LinkedHashMap();
     Map<String, IndexEntry> groupIndex = new LinkedHashMap();
     long indexPositionPosition;
     boolean compressed;
 
-    public TDFWriter(File file,
+    public TDFWriter(File f,
                      String genomeId,
                      TrackType trackType,
                      String trackLine, String[] trackNames,
                      Collection<WindowFunction> windowFunctions,
                      boolean compressed) {
 
-        this.file = file;
+        if (f.getName().endsWith(".tdf")) {
+            this.file = f;
+        } else {
+            this.file = new File(f.getAbsolutePath() + ".tdf");
+        }
         this.compressed = compressed;
 
-        String filename = file.getAbsolutePath();
-
-        String fullpath = new File(filename).getAbsolutePath();
-        if (!this.file.getAbsolutePath().equals(fullpath)) {
-            throw new IllegalArgumentException("Filename mismatch.  Expected " + this.file.getAbsolutePath() +
-                    " argument: " + filename);
-        }
 
         try {
-            fos = new LEBufferedDataOutputStream(new FileOutputStream(file));
+            fos = new BufferedOutputStream(new FileOutputStream(file));
             writeHeader(genomeId, trackType, trackLine, trackNames, windowFunctions);
 
-            IBFGroup rootGroup = new IBFGroup("/");
+            TDFGroup rootGroup = new TDFGroup("/");
             groupCache.put(rootGroup.getName(), rootGroup);
 
-
         } catch (IOException ex) {
-            log.error("Error creating file: " + filename, ex);
-            throw new RuntimeException("Error creating file: " + filename, ex);
+            log.error("Error creating file: " + file.getAbsolutePath(), ex);
+            throw new DataLoadException("Error creating file", file.getAbsolutePath());
         }
 
     }
@@ -95,39 +93,41 @@ public class TDFWriter {
 
         // Magic number -- 4 bytes
         byte[] magicNumber = new byte[]{'T', 'D', 'F', '3'};
-        fos.write(magicNumber);
-        fos.writeInt(version);
+
+        BufferedByteWriter buffer = new BufferedByteWriter(24);
+        buffer.put(magicNumber);
+        buffer.putInt(version);
         // Reserve space for the master index pointer and byte count.
         // The actual values will be written at the end
-        indexPositionPosition = fos.bytesWritten();
-        fos.writeLong(0l);
-        fos.writeInt(0);
-
-        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-        LEDataOutputStream dos = new LEDataOutputStream(buffer);
+        indexPositionPosition = buffer.bytesWritten();
+        buffer.putLong(0l);
+        buffer.putInt(0);
+        write(buffer.getBytes());
 
+        buffer = new BufferedByteWriter(24);
         // Window function definition
-        dos.writeInt(windowFunctions.size());
+        buffer.putInt(windowFunctions.size());
+
         for (WindowFunction wf : windowFunctions) {
-            writeString(dos, wf.toString());
+            buffer.putNullTerminatedString(wf.toString());
         }
 
         // Track type
-        writeString(dos, trackType.toString());
+        buffer.putNullTerminatedString(trackType.toString());
 
         // Reserved space for the track line
         byte[] trackLineBuffer = bufferString(trackLine, 1024);
-        dos.write(trackLineBuffer);
+        buffer.put(trackLineBuffer);
 
         // Track names
-        dos.writeInt(trackNames.length);
+        buffer.putInt(trackNames.length);
         for (String nm : trackNames) {
-            writeString(dos, nm);
+            buffer.putNullTerminatedString(nm);
         }
 
         // Fields below added in version 3
         // Genome id
-        writeString(dos, genomeId);
+        buffer.putNullTerminatedString(genomeId);
 
         // Flags
         int flags = 0;
@@ -136,15 +136,15 @@ public class TDFWriter {
         } else {
             flags &= ~TDFReader.GZIP_FLAG;
         }
-        dos.writeInt(flags);
-
+        buffer.putInt(flags);
 
-        byte[] bytes = buffer.toByteArray();
+        byte [] bytes = buffer.getBytes();
 
-        this.fos.writeInt(bytes.length);
-        this.fos.write(bytes);
+        writeInt(bytes.length);
+        write(buffer.getBytes());
     }
 
+
     /**
      * Write out the group and dataset index and close the underlying file.
      */
@@ -154,9 +154,9 @@ public class TDFWriter {
             writeDatasets();
             writeGroups();
 
-            long indexPosition = fos.bytesWritten();
+            long indexPosition = bytesWritten;
             writeIndex();
-            int nbytes = (int) (fos.bytesWritten() - indexPosition);
+            int nbytes = (int) (bytesWritten - indexPosition);
 
             fos.close();
 
@@ -173,59 +173,59 @@ public class TDFWriter {
             raf.getChannel().position(indexPositionPosition);
 
             // Write as little endian
-            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-            LEDataOutputStream dos = new LEDataOutputStream(buffer);
-            dos.writeLong(indexPosition);
-            dos.writeInt(nbytes);
-            raf.write(buffer.toByteArray());
+            BufferedByteWriter buffer = new BufferedByteWriter();
+            buffer.putLong(indexPosition);
+            buffer.putInt(nbytes);
+            raf.write(buffer.getBytes());
             raf.close();
         } catch (IOException ex) {
             ex.printStackTrace();
         }
     }
 
-    public IBFGroup getGroup(String name) {
+    public TDFGroup getGroup(String name) {
         return groupCache.get(name);
     }
 
-    public IBFGroup getRootGroup() {
+    public TDFGroup getRootGroup() {
         if (!groupCache.containsKey("/")) {
-            groupCache.put("/", new IBFGroup("/"));
+            groupCache.put("/", new TDFGroup("/"));
         }
         return groupCache.get("/");
     }
 
-    public IBFGroup createGroup(String name) {
+    public TDFGroup createGroup(String name) {
         if (groupCache.containsKey(name)) {
             throw new RuntimeException("Group: " + name + " already exists");
         }
-        IBFGroup group = new IBFGroup(name);
+        TDFGroup group = new TDFGroup(name);
         groupCache.put(name, group);
         return group;
     }
 
-    public IBFDataset createDataset(String name, IBFDataset.DataType dataType,
+    public TDFDataset createDataset(String name, TDFDataset.DataType dataType,
                                     int tileWidth, int nTiles) {
 
         if (datasetCache.containsKey(name)) {
             throw new RuntimeException("Dataset: " + name + " already exists");
         }
 
-        IBFDataset ds = new IBFDataset(name, dataType, tileWidth, nTiles);
+        TDFDataset ds = new TDFDataset(name, dataType, tileWidth, nTiles);
         datasetCache.put(name, ds);
         return ds;
     }
 
     // Note this will only work for "fixed step" format.  Others need location arrays
     // Tile layout
-    public void writeTile(String dsId, int tileNumber, IBFTile tile) throws IOException {
 
-        IBFDataset dataset = datasetCache.get(dsId);
+    public void writeTile(String dsId, int tileNumber, TDFTile tile) throws IOException {
+
+        TDFDataset dataset = datasetCache.get(dsId);
         if (dataset == null) {
             throw new java.lang.NoSuchFieldError("Dataset: " + dsId + " doese not exist.  " +
                     "Call createDataset first");
         }
-        long pos = fos.bytesWritten();
+        long pos = bytesWritten;
 
         if (tileNumber < dataset.tilePositions.length) {
             dataset.tilePositions[tileNumber] = pos;
@@ -233,18 +233,15 @@ public class TDFWriter {
             // Write the tile contents to a byte buffer first,  so we can optionally gzip it
 
 
-            ByteArrayOutputStream bos = new ByteArrayOutputStream(64000);
-            LEBufferedDataOutputStream bDOS = new LEBufferedDataOutputStream(bos);
-            tile.writeTo(bDOS);
-            bDOS.flush();
-            bDOS.close();
+            BufferedByteWriter buffer = new BufferedByteWriter();
+            tile.writeTo(buffer);
 
-            byte[] bytes = bos.toByteArray();
-            if(compressed) {
+            byte[] bytes = buffer.getBytes();
+            if (compressed) {
                 bytes = CompressionUtils.compress(bytes);
             }
 
-            fos.write(bytes);
+            write(bytes);
             int nBytes = bytes.length;
 
             //tile.writeTo(fos);
@@ -263,19 +260,27 @@ public class TDFWriter {
     }
 
     private void writeGroups() throws IOException {
-        for (IBFGroup group : groupCache.values()) {
-            long position = fos.bytesWritten();
-            group.write(fos);
-            int nBytes = (int) (fos.bytesWritten() - position);
+        for (TDFGroup group : groupCache.values()) {
+            long position = bytesWritten;
+
+            BufferedByteWriter buffer = new BufferedByteWriter();
+            group.write(buffer);
+            write(buffer.getBytes());
+
+            int nBytes = (int) (bytesWritten - position);
             groupIndex.put(group.getName(), new IndexEntry(position, nBytes));
         }
     }
 
     private void writeDatasets() throws IOException {
-        for (IBFDataset dataset : datasetCache.values()) {
-            long position = fos.bytesWritten();
-            dataset.write(fos);
-            int nBytes = (int) (fos.bytesWritten() - position);
+        for (TDFDataset dataset : datasetCache.values()) {
+            long position = bytesWritten;
+
+            BufferedByteWriter buffer = new BufferedByteWriter();
+            dataset.write(buffer);
+            write(buffer.getBytes());
+
+            int nBytes = (int) (bytesWritten - position);
             datasetIndex.put(dataset.getName(), new IndexEntry(position, nBytes));
 
         }
@@ -283,44 +288,31 @@ public class TDFWriter {
 
     private void writeIndex() throws IOException {
 
-        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-        LEDataOutputStream dos = new LEDataOutputStream(buffer);
+        BufferedByteWriter buffer = new BufferedByteWriter();
 
         // Now write out dataset index
-        dos.writeInt(datasetIndex.size());
+        buffer.putInt(datasetIndex.size());
         for (Map.Entry<String, IndexEntry> entry : datasetIndex.entrySet()) {
-            writeString(dos, entry.getKey());
-            dos.writeLong(entry.getValue().position);
-            dos.writeInt(entry.getValue().nBytes);
+            buffer.putNullTerminatedString(entry.getKey());
+            buffer.putLong(entry.getValue().position);
+            buffer.putInt(entry.getValue().nBytes);
         }
 
         // group index
         System.out.println("Group idx: " + groupIndex.size());
-        dos.writeInt(groupIndex.size());
+        buffer.putInt(groupIndex.size());
         for (Map.Entry<String, IndexEntry> entry : groupIndex.entrySet()) {
-            writeString(dos, entry.getKey());
-            dos.writeLong(entry.getValue().position);
-            dos.writeInt(entry.getValue().nBytes);
+            buffer.putNullTerminatedString(entry.getKey());
+            buffer.putLong(entry.getValue().position);
+            buffer.putInt(entry.getValue().nBytes);
         }
 
-        byte[] bytes = buffer.toByteArray();
+        byte[] bytes = buffer.getBytes();
 
-        this.fos.write(bytes);
+        write(bytes);
     }
 
-    /**
-     * Write a string as a null terminated character sequence.
-     * <p/>
-     * IGV requires all strings to be ascii,  so single byte storaged is enough
-     *
-     * @param dos
-     * @param s
-     * @throws java.io.IOException
-     */
-    public void writeString(LEDataOutputStream dos, String s) throws IOException {
-        dos.write(s.getBytes());
-        dos.writeByte(0);
-    }
+
 
     private byte[] bufferString(String str, int bufferSize) throws IOException {
         byte[] buffer = new byte[bufferSize];
@@ -334,6 +326,20 @@ public class TDFWriter {
 
     }
 
+
+    private void writeInt(int v) throws IOException {
+        fos.write((v >>> 0) & 0xFF);
+        fos.write((v >>> 8) & 0xFF);
+        fos.write((v >>> 16) & 0xFF);
+        fos.write((v >>> 24) & 0xFF);
+        bytesWritten += 4;
+    }
+
+    private void write(byte[] bytes) throws IOException {
+        fos.write(bytes);
+        bytesWritten += bytes.length;
+    }
+
     class IndexEntry {
 
         long position;
diff --git a/src/org/broad/igv/tdf/TileFactory.java b/src/org/broad/igv/tdf/TileFactory.java
index cd2097d..3e77dd4 100644
--- a/src/org/broad/igv/tdf/TileFactory.java
+++ b/src/org/broad/igv/tdf/TileFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,31 +23,34 @@
 package org.broad.igv.tdf;
 
 
+import org.broad.igv.util.StringUtils;
+
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.util.zip.GZIPInputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 
 /**
  * @author jrobinso
  */
 public class TileFactory {
 
-    public static IBFTile createTile(byte[] buffer, int nSamples, boolean gzipped) throws IOException {
+    public static TDFTile createTile(byte[] buffer, int nSamples) throws IOException {
+
+        ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
+        byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
 
-        ByteArrayInputStream bos = new ByteArrayInputStream(buffer);
-        LEDataInputStream dis = new LEDataInputStream(bos);
-        String typeString = dis.readString();
-        IBFTile.Type type = IBFTile.Type.valueOf(typeString);
+        String typeString = StringUtils.readString(byteBuffer);
+        TDFTile.Type type = TDFTile.Type.valueOf(typeString);
 
         switch (type) {
             case fixedStep:
-                return new IBFFixedTile(dis, nSamples);
+                return new TDFFixedTile(byteBuffer, nSamples);
             case variableStep:
-                return new IBFVaryTile(dis, nSamples);
+                return new TDFVaryTile(byteBuffer, nSamples);
             case bed:
             case bedWithName:
-                return new TDFBedTile(dis, nSamples, type);
+                return new TDFBedTile(byteBuffer, nSamples, type);
             default:
                 throw new RuntimeException("Unknown tile type: " + type.toString());
         }
diff --git a/src/org/broad/igv/tdf/TileManager.java b/src/org/broad/igv/tdf/TileManager.java
index 62b3214..626a01f 100644
--- a/src/org/broad/igv/tdf/TileManager.java
+++ b/src/org/broad/igv/tdf/TileManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -37,9 +37,9 @@ public class TileManager {
         this.reader = reader;
     }
 
-    public List<IBFTile> getTiles(String chr, int start, int end, int zoom) {
+    public List<TDFTile> getTiles(String chr, int start, int end, int zoom) {
 
-        IBFDataset ds = reader.getDataset(chr, zoom, WindowFunction.mean);
+        TDFDataset ds = reader.getDataset(chr, zoom, WindowFunction.mean);
         if (ds != null) {
             return ds.getTiles(start, end);
         }
@@ -47,19 +47,19 @@ public class TileManager {
     }
 
     /*
-    private IBFTile computeTile(String chr, int start, int end, int zoom) {
+    private TDFTile computeTile(String chr, int start, int end, int zoom) {
         String dsName = "/" + chr + "/raw";
 
-        IBFDataset ds = reader.getDataset(dsName);
+        TDFDataset ds = reader.getDataset(dsName);
         if (ds != null) {
 
             double binSize = tileWidth / 700;
             int binSizeInt = (int) binSize;
 
-            List<IBFTile> tiles = ds.getTiles(startLocation, endLocation);
+            List<TDFTile> tiles = ds.getTiles(startLocation, endLocation);
             if (tiles.size() > 0) {
 
-                for (IBFTile tile : tiles) {
+                for (TDFTile tile : tiles) {
                     // Tile of raw data
                     if (tile != null && tile.getSize() > 0) {
 
diff --git a/src/org/broad/igv/tdf/notes.txt b/src/org/broad/igv/tdf/notes.txt
index ed3cd7a..404d09a 100644
--- a/src/org/broad/igv/tdf/notes.txt
+++ b/src/org/broad/igv/tdf/notes.txt
@@ -19,8 +19,6 @@
     track line (string)
     # of tracks
     [track names]
-    genomeId
-    flags
 
     FixedTile
     ---------
@@ -30,6 +28,18 @@
     Span
     Data
 
+    Features
+    ---------
+    magic number    (32 bit int)  <= 'F','E', 'A', 'T'
+    format          (string)  <= bed, gff, other
+    [chr             (string)
+     start           (int)
+     rest of record  (string)]
+
+    FeatureIndex
+    ------------
+    
+
     Dataset
     -------
     # attributes
@@ -39,7 +49,7 @@
     tile width
     # tiles
     [tile position
-     tile size (bytes)]
+     tile size (bytes)]  <= THIS IS NOT NECCESSARY
 
     Group
     ------
@@ -49,14 +59,15 @@
 
     Master Index
     -----------
+    feature index position (long)  (-1 if no features)
     # datasets
     [dataset name (string)
      position (long)
-     size in bytes (int)]
+     size in bytes (int)]    <= THIS IS NOT NECCESSARY
     # groups
     [group name
      position (long)
-     size in bytes (int)]
+     size in bytes (int)]  <= THIS IS NOT NECCESSARY
 
 
 
diff --git a/src/org/broad/igv/tools/Accumulator.java b/src/org/broad/igv/tools/Accumulator.java
index 72696cb..db37ba5 100644
--- a/src/org/broad/igv/tools/Accumulator.java
+++ b/src/org/broad/igv/tools/Accumulator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,81 +22,80 @@
  */
 package org.broad.igv.tools;
 
-import cern.colt.list.DoubleArrayList;
-import cern.jet.random.engine.DRand;
-import hep.aida.bin.QuantileBin1D;
+import org.broad.igv.util.collections.DoubleArrayList;
+import org.apache.commons.math.stat.StatUtils;
+import org.apache.log4j.Logger;
 import org.broad.igv.track.WindowFunction;
 
 import java.util.*;
 
 /**
+ * Estimating percentiles -- weighted average of multiple estimates
+ *
  * @author jrobinso
  */
 public class Accumulator {
 
-    static Set<WindowFunction> percentileFunctions = new HashSet();
+    static Set<WindowFunction> PERCENTILE_WINDOW_FUNCTIONS = new HashSet();
+    public static int MAX_VALUE_COUNT = 100000;
+    private static Logger log = Logger.getLogger(Accumulator.class);
 
     static {
-        percentileFunctions.add(WindowFunction.median);
-        percentileFunctions.add(WindowFunction.percentile2);
-        percentileFunctions.add(WindowFunction.percentile10);
-        percentileFunctions.add(WindowFunction.percentile90);
-        percentileFunctions.add(WindowFunction.percentile98);
+        PERCENTILE_WINDOW_FUNCTIONS.add(WindowFunction.median);
+        PERCENTILE_WINDOW_FUNCTIONS.add(WindowFunction.percentile2);
+        PERCENTILE_WINDOW_FUNCTIONS.add(WindowFunction.percentile10);
+        PERCENTILE_WINDOW_FUNCTIONS.add(WindowFunction.percentile90);
+        PERCENTILE_WINDOW_FUNCTIONS.add(WindowFunction.percentile98);
     }
 
 
     boolean isFinished = false;
-    QuantileBin1D quantileBin;
-    Map<WindowFunction, Float> values;
+
     List<WindowFunction> windowFunctions;
     List<WindowFunction> quantileFunctions;
-    DoubleArrayList percentiles;
-    float min = Float.MAX_VALUE;
-    float max = Float.MIN_VALUE;
+    DoubleArrayList values = null;
     float sum = 0.0f;
     int nPts = 0;
 
-    public Accumulator() {
-        this(Arrays.asList(WindowFunction.mean));
 
-    }
+    Map<WindowFunction, List<PercentileValue>> percentiles = new HashMap();
 
-    public Accumulator(Collection<WindowFunction> windowFunctions) {
 
+    float min = Float.NaN;
+    float max = Float.NaN;
+    float mean = Float.NaN;
+    float median = Float.NaN;
+    float percentile2 = Float.NaN;
+    float percentile10 = Float.NaN;
+    float percentile90 = Float.NaN;
+    float percentile98 = Float.NaN;
+
+
+    public Accumulator(Collection<WindowFunction> windowFunctions) {
         this.windowFunctions = new ArrayList(windowFunctions);
         quantileFunctions = new ArrayList();
         for (WindowFunction wf : windowFunctions) {
-            if (percentileFunctions.contains(wf)) {
+            if (PERCENTILE_WINDOW_FUNCTIONS.contains(wf)) {
                 quantileFunctions.add(wf);
-            }
-        }
-
-        if (!quantileFunctions.isEmpty()) {
-            DRand rand = new DRand(new Date());
-            quantileBin = new QuantileBin1D(false,
-                    Integer.MAX_VALUE,
-                    .001,
-                    .01,
-                    quantileFunctions.size(),
-                    rand,
-                    false,
-                    false,
-                    2);
-            percentiles = new DoubleArrayList();
-            for (WindowFunction wf : quantileFunctions) {
-                percentiles.add(getPercentile(wf));
+                if (values == null) {
+                    values = new DoubleArrayList();
+                }
             }
         }
     }
 
     public void add(float v) {
         if (!Float.isNaN(v)) {
-            min = Math.min(min, v);
-            max = Math.max(max, v);
+            min = Float.isNaN(min) ? v : Math.min(min, v);
+            max = Float.isNaN(max) ? v : Math.max(max, v);
             sum += v;
             nPts++;
-            if (quantileBin != null) {
-                quantileBin.add(v);
+            if (values != null) {
+                values.add(v);
+                if (values.size() > MAX_VALUE_COUNT) {
+                    computePercentiles();
+                    values.clear();
+                }
             }
         }
     }
@@ -108,96 +107,154 @@ public class Accumulator {
             return;
         }
 
-        values = new HashMap(windowFunctions.size());
-        float mean = sum / nPts;
-        values.put(WindowFunction.min, min);
-        values.put(WindowFunction.max, max);
-        values.put(WindowFunction.mean, mean);
-        values.put(WindowFunction.count, (float) nPts);
+        mean = Float.isNaN(sum) ? Float.NaN : sum / nPts;
 
-        if (!quantileFunctions.isEmpty()) {
+        if (values != null) {
             if (nPts == 1) {
-                for (int i = 0; i < quantileFunctions.size(); i++) {
-                    values.put(quantileFunctions.get(i), mean);
+                for (WindowFunction wf : quantileFunctions) {
+                    setValue(wf, mean);
                 }
             } else {
+                if (values.size() > 1) {
+                    computePercentiles();
+                }
+                for (WindowFunction wf : quantileFunctions) {
 
-                // Sort percentiles by ascending phi
-                percentiles.sort();
-                DoubleArrayList quantiles = quantileBin.quantiles(percentiles);
-                try {
-                    for (int i = 0; i < quantileFunctions.size(); i++) {
-                        float v = (float) quantiles.get(i);
-                        if (Float.isInfinite(v)) {
-                            v = Float.NaN;
+                    List<PercentileValue> pList = percentiles.get(wf);
+                    float v = Float.NaN; // <= Default,
+                    if (pList != null && pList.size() > 0) {
+                        double weightedSum = 0;
+                        double sumOfWeights = 0;
+                        for (PercentileValue pv : pList) {
+                            double weight = (double) pv.nPoints / nPts;
+                            sumOfWeights += weight;
+                            weightedSum += weight * pv.value;
                         }
-                        double p = percentiles.get(i);
-                        WindowFunction wf = this.getWindowFunction(p);
-                        values.put(wf, v);
+                        v = (float) (weightedSum / sumOfWeights);
                     }
-                } catch (Exception e) {
-                    System.out.println("Error computing quantiles.  qf = " + quantileBin.toString());
-                    for (int i = 0; i < quantileFunctions.size(); i++) {
-                        values.put(quantileFunctions.get(i), Float.NaN);
+                    setValue(wf, v);
+
+                }
+
+            }
+        }
+        values = null;
+        isFinished = true;
+
+    }
+
+    private void computePercentiles() {
+        if (values != null) {
+            double[] valueArray = values.toArray();
+            for (WindowFunction wf : quantileFunctions) {
+                double p = this.getPercentile(wf);
+                if (p > 0) {
+                    float v = (float) StatUtils.percentile(valueArray, p);
+                    if (Float.isInfinite(v)) {
+                        log.error("Infinite percentile (" + wf + ")");
+                    } else {
+                        List<PercentileValue> pList = percentiles.get(wf);
+                        if (pList == null) {
+                            pList = new ArrayList();
+                            percentiles.put(wf, pList);
+                        }
+                        pList.add(new PercentileValue(valueArray.length, v));
                     }
                 }
             }
         }
 
-        // Release quantile bin
-        quantileBin = null;
-        isFinished = true;
+    }
+
+    private void setValue(WindowFunction wf, float value) {
+        switch (wf) {
+            case mean:
+                mean = value;
+                break;
+            case median:
+                median = value;
+                break;
+            case min:
+                min = value;
+                break;
+            case max:
+                max = value;
+                break;
+            case percentile2:
+                percentile2 = value;
+                break;
+            case percentile10:
+                percentile10 = value;
+                break;
+            case percentile90:
+                percentile90 = value;
+                break;
+            case percentile98:
+                percentile98 = value;
+                break;
+            default:
+                System.err.println("Unexpected window function: " + wf.toString());
+        }
+
 
     }
 
     public float getValue(WindowFunction wf) {
-        if (values == null) {
-            throw new java.lang.IllegalStateException(
-                    "Accumulator is still open.  Call finish() before getValue()");
-        }
-        try {
-            return values.get(wf);
-        } catch (Exception e) {
-            e.printStackTrace();
-            return Float.NaN;
+
+        switch (wf) {
+            case mean:
+                return mean;
+            case median:
+                return median;
+            case min:
+                return min;
+            case max:
+                return max;
+            case percentile2:
+                return percentile2;
+            case percentile10:
+                return percentile10;
+            case percentile90:
+                return percentile90;
+            case percentile98:
+                return percentile98;
+            case count:
+                return nPts;
+            default:
+                System.err.println("Unexpected window function: " + wf.toString());
+
         }
+        return Float.NaN;
+
     }
 
 
     public double getPercentile(WindowFunction wf) {
         switch (wf) {
             case percentile2:
-                return 0.02;
+                return 2;
             case percentile10:
-                return 0.1;
+                return 10;
             case percentile90:
-                return 0.9;
+                return 90;
             case percentile98:
-                return 0.98;
+                return 98;
             case median:
-                return 0.5;
+                return 50;
             default:
                 return -1.0;
         }
     }
 
-    public WindowFunction getWindowFunction(double percentile) {
-        if (floatEquals(0.02, percentile, 1.0e-3)) {
-            return WindowFunction.percentile2;
-        } else if (floatEquals(0.1, percentile, 1.0e-3)) {
-            return WindowFunction.percentile10;
-        } else if (floatEquals(0.90, percentile, 1.0e-3)) {
-            return WindowFunction.percentile90;
-        } else if (floatEquals(0.98, percentile, 1.0e-3)) {
-            return WindowFunction.percentile98;
-        } else if (floatEquals(0.5, percentile, 1.0e-3)) {
-            return WindowFunction.median;
-        }
-        return null;
 
-    }
+    class PercentileValue {
+        int nPoints;
+        double value;
 
-    public boolean floatEquals(double d1, double d2, double tolerance) {
-        return (Math.abs(d1 - d2) < tolerance);
+        PercentileValue(int nPoints, double value) {
+            this.nPoints = nPoints;
+            this.value = value;
+        }
     }
+
 }
diff --git a/src/org/broad/igv/tools/CommandLineStatusMonitor.java b/src/org/broad/igv/tools/CommandLineStatusMonitor.java
index b8397f9..fde24bc 100644
--- a/src/org/broad/igv/tools/CommandLineStatusMonitor.java
+++ b/src/org/broad/igv/tools/CommandLineStatusMonitor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/tools/CoverageCounter.java b/src/org/broad/igv/tools/CoverageCounter.java
index 57e7bc8..0dd3ef1 100644
--- a/src/org/broad/igv/tools/CoverageCounter.java
+++ b/src/org/broad/igv/tools/CoverageCounter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,19 +23,24 @@
 package org.broad.igv.tools;
 
 import net.sf.samtools.util.CloseableIterator;
+import org.broad.igv.feature.Chromosome;
+import org.broad.igv.feature.Genome;
+import org.broad.igv.feature.Strand;
 import org.broad.igv.sam.Alignment;
 import org.broad.igv.sam.AlignmentBlock;
 import org.broad.igv.sam.reader.AlignmentQueryReader;
 import org.broad.igv.sam.reader.SamQueryReaderFactory;
 import org.broad.igv.tools.parsers.DataConsumer;
-import org.broad.igv.tools.parsers.UnsortedException;
 import org.broad.igv.track.TrackType;
 
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
-import java.io.IOException;
 
 /**
  *   TODO -- normalize option
@@ -51,58 +56,78 @@ import java.io.IOException;
  */
 public class CoverageCounter {
 
-    String alignmentFile;
-    DataConsumer consumer;
-    float[] buffer = new float[1];
-    int windowSize = 1;
+    private String alignmentFile;
+    private DataConsumer consumer;
+    private float[] buffer;
+    private int windowSize = 1;
     // TODO -- make mapping qulaity a parameter
-    int minMappingQuality = 0;
-    String chr;
-    int start;
-    int end;
-    int extFactor;
-
-    public CoverageCounter(String alignmentFile, DataConsumer consumer, int windowSize, int extFactor) {
+    private int minMappingQuality = 0;
+    private int strandOption = -1;
+    private int extFactor;
+    private int totalCount = 0;
+    private File wigFile = null;
+    private WigWriter wigWriter = null;
+    private Genome genome;
+
+    public CoverageCounter(String alignmentFile,
+                           DataConsumer consumer,
+                           int windowSize,
+                           int extFactor,
+                           File wigFile,
+                           Genome genome,
+                           int strandOption) {
         this.alignmentFile = alignmentFile;
         this.consumer = consumer;
         this.windowSize = windowSize;
         this.extFactor = extFactor;
+        this.wigFile = wigFile;
+        this.genome = genome;
+        this.strandOption = strandOption;
+        buffer = strandOption < 0 ? new float[1] : new float[2];
     }
 
     private boolean passFilter(Alignment alignment) {
 
+        if (strandOption > 0 && alignment.getFragmentStrand(strandOption) == Strand.NONE) {
+            return false;
+        }
         return alignment.isMapped() && !alignment.isDuplicate() && alignment.getMappingQuality() >= minMappingQuality;
     }
 
     public void parse() {
 
-        System.out.println("Computing coverage.  File = " + alignmentFile);
-        System.out.println("Window size = " + windowSize);
-        System.out.println("Ext factor = " + extFactor);
-
         int tolerance = (int) (windowSize * (Math.floor(extFactor / windowSize) + 2));
         consumer.setSortTolerance(tolerance);
 
         AlignmentQueryReader reader = null;
         CloseableIterator<Alignment> iter = null;
 
-
         String lastChr = "";
         ReadCounter counter = null;
 
+
         try {
+
+            if (wigFile != null) {
+                wigWriter = new WigWriter(wigFile, windowSize);
+            }
+
             reader = SamQueryReaderFactory.getReader(alignmentFile, false);
-            iter = chr == null ? reader.iterator() : reader.query(chr, start, end, false);
+            iter = reader.iterator();
+
             while (iter != null && iter.hasNext()) {
                 Alignment alignment = iter.next();
                 if (passFilter(alignment)) {
 
-                    String alignmentChr = alignment.getChromosome();
+                    totalCount++;
 
+                    String alignmentChr = alignment.getChr();
 
                     // Close all counters with position < alignment.getStart()
                     if (alignmentChr.equals(lastChr)) {
-                        counter.closeBucketsBefore(alignment.getAlignmentStart() - tolerance);
+                        if (counter != null) {
+                            counter.closeBucketsBefore(alignment.getAlignmentStart() - tolerance);
+                        }
                     } else {
                         if (counter != null) {
                             counter.closeBucketsBefore(Integer.MAX_VALUE);
@@ -113,7 +138,7 @@ public class CoverageCounter {
 
                     AlignmentBlock[] blocks = alignment.getAlignmentBlocks();
                     if (blocks != null) {
-                        for (AlignmentBlock block : alignment.getAlignmentBlocks()) {
+                        for (AlignmentBlock block : blocks) {
 
                             int adjustedStart = block.getStart();
                             int adjustedEnd = block.getEnd();
@@ -124,6 +149,7 @@ public class CoverageCounter {
                             }
 
                             for (int pos = adjustedStart; pos < adjustedEnd; pos++) {
+
                                 counter.incrementCount(pos);
                             }
                         }
@@ -143,14 +169,18 @@ public class CoverageCounter {
                 }
 
             }
-        } finally {
-
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        finally {
             if (counter != null) {
                 counter.closeBucketsBefore(Integer.MAX_VALUE);
             }
+
+            consumer.setAttribute("totalCount", String.valueOf(totalCount));
             consumer.parsingComplete();
 
-            if(reader != null) {
+            if (reader != null) {
                 try {
                     reader.close();
                 } catch (IOException e) {
@@ -158,9 +188,13 @@ public class CoverageCounter {
                 }
             }
 
-            if(iter != null) {
+            if (iter != null) {
                 iter.close();
             }
+
+            if (wigWriter != null) {
+                wigWriter.close();
+            }
         }
     }
 
@@ -181,16 +215,43 @@ public class CoverageCounter {
             counts.get(bucket).increment();
         }
 
+        void incrementNegCount(int position) {
+            Integer bucket = position / windowSize;
+            if (!counts.containsKey(bucket)) {
+                counts.put(bucket, new Counter());
+            }
+            counts.get(bucket).incrementNeg();
+        }
+
         void closeBucketsBefore(int position) {
             List<Integer> bucketsToClose = new ArrayList();
 
             Integer bucket = position / windowSize;
             for (Map.Entry<Integer, Counter> entry : counts.entrySet()) {
                 if (entry.getKey() < bucket) {
-                    buffer[0] = ((float) entry.getValue().getCount()) / windowSize;
+
+                    // Divide total count by window size.  This is the average count per
+                    // base over the window,  so 30x coverage remains 30x irrespective of window size.
                     int bucketStartPosition = entry.getKey() * windowSize;
                     int bucketEndPosition = bucketStartPosition + windowSize;
+                    if (genome != null) {
+                        Chromosome chromosome = genome.getChromosome(chr);
+                        if (chromosome != null) {
+                            bucketEndPosition = Math.min(bucketEndPosition, chromosome.getLength());
+                        }
+                    }
+                    int bucketSize = bucketEndPosition - bucketStartPosition;
+
+                    buffer[0] = ((float) entry.getValue().getCount()) / bucketSize;
+
+                    if (strandOption > 0) {
+                        buffer[1] = ((float) entry.getValue().getCount()) / bucketSize;
+                    }
+
                     consumer.addData(chr, bucketStartPosition, bucketEndPosition, buffer, null);
+                    if (wigWriter != null) {
+                        wigWriter.addData(chr, bucketStartPosition, bucketEndPosition, buffer, null);
+                    }
                     bucketsToClose.add(entry.getKey());
                 }
             }
@@ -206,56 +267,93 @@ public class CoverageCounter {
     static class Counter {
 
         int count = 0;
+        int negCount = 0;
 
         void increment() {
             count++;
         }
 
+        void incrementNeg() {
+            negCount++;
+        }
+
         int getCount() {
             return count;
         }
-    }
 
-    public static void main(String[] args) {
-        String bamFile = "/Volumes/igv/dev/cov/TCGA-23-1116-01A-01W.bam";
-        DataConsumer con = new TestConsumer();
-        int windowSize = 1;
-        int extFactor = 0;
-
-        CoverageCounter cc = new CoverageCounter(bamFile, con, windowSize, extFactor);
-        cc.chr = "chr1";
-        cc.start = 43658939;
-        cc.end = 43660824;
-        cc.parse();
+        int getNegCount() {
+            return negCount;
+        }
     }
 
-    static class TestConsumer implements DataConsumer {
-
-        public void setType(String type) {
-            System.out.println("Type = " + type);
+    /**
+     * Creates a vary step wig file
+     */
+    class WigWriter implements DataConsumer {
+        String lastChr = null;
+        int lastPosition = 0;
+        int step;
+        int span;
+        PrintWriter pw;
+
+        WigWriter(File file, int step) throws IOException {
+            this.step = step;
+            this.span = step;
+            pw = new PrintWriter(new FileWriter(file));
         }
 
         public void addData(String chr, int start, int end, float[] data, String name) {
-            if (end < start) {
-                System.out.println("end < start");
-                throw new UnsortedException("Data is unsorted.");
+
+            if (genome.getChromosome(chr) == null) {
+                return;
+            }
+
+            if (data[0] == 0 || end <= start) {
+                return;
+            }
+
+            int dataSpan = end - start;
+
+            if (chr == null || !chr.equals(lastChr) || dataSpan != span) {
+                span = dataSpan;
+                outputStepLine(chr, start + 1);
             }
-            System.out.println(chr + "\t" + start + "\t" + end + "\t" + name + "\t" + data[0]);
+            pw.println((start + 1) + "\t" + data[0]);
+            lastPosition = start;
+            lastChr = chr;
+
+        }
+
+        private void close() {
+            pw.close();
+
+        }
+
+        private void outputStepLine(String chr, int start) {
+            pw.println("variableStep chrom=" + chr + " span=" + span);
         }
 
-        public void newChromosome(String chr) {
-            System.out.println();
-            System.out.println("New chromosome:" + chr);
+        public void setType(String type) {
+            //To change body of implemented methods use File | Settings | File Templates.
         }
 
+
         public void parsingComplete() {
+            //To change body of implemented methods use File | Settings | File Templates.
         }
 
         public void setTrackParameters(TrackType trackType, String trackLine, String[] trackNames) {
+            //To change body of implemented methods use File | Settings | File Templates.
         }
 
         public void setSortTolerance(int tolerance) {
-            throw new UnsupportedOperationException("Not supported yet.");
+            //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        public void setAttribute(String key, String value) {
+            //To change body of implemented methods use File | Settings | File Templates.
         }
     }
+
+
 }
diff --git a/src/org/broad/igv/tools/GenomeDesc.java b/src/org/broad/igv/tools/GenomeDesc.java
index 0936cc3..8be8c17 100644
--- a/src/org/broad/igv/tools/GenomeDesc.java
+++ b/src/org/broad/igv/tools/GenomeDesc.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/tools/IgvTools.java b/src/org/broad/igv/tools/IgvTools.java
index 74dc5cd..febc56d 100644
--- a/src/org/broad/igv/tools/IgvTools.java
+++ b/src/org/broad/igv/tools/IgvTools.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,298 +23,493 @@
 package org.broad.igv.tools;
 
 import jargs.gnu.CmdLineParser;
-import org.broad.igv.IGVConstants;
-import org.broad.igv.data.SegmentedDataWriter;
+import org.broad.igv.Globals;
+import org.broad.igv.data.seg.SegmentedDataWriter;
 import org.broad.igv.feature.GFFParser;
+import org.broad.igv.feature.Genome;
 import org.broad.igv.feature.GenomeManager;
-import org.broad.igv.preprocess.old.IGVPreprocessor;
 import org.broad.igv.sam.reader.AlignmentIndexer;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.tools.parsers.GCTtoIGVConverter;
 import org.broad.igv.tools.sort.Sorter;
-import org.broad.igv.ui.AboutDialog;
-import org.broad.igv.ui.IGVModel;
+import org.broad.igv.track.WindowFunction;
+import org.broad.igv.track.tribble.CodecFactory;
 import org.broad.igv.util.FileUtils;
 import org.broad.igv.util.ParsingUtils;
-
+import org.broad.igv.util.ResourceLocator;
+import org.broad.tribble.FeatureCodec;
+import org.broad.tribble.index.IndexCreator;
+import org.broad.tribble.index.interval.IntervalIndexCreator;
+import org.broad.tribble.index.linear.LinearIndex;
+import org.broad.tribble.index.linear.LinearIndexCreator;
+
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
 
 /**
+ * Command line parser for "igvtools".
+ *
  * @author jrobinso
  */
 public class IgvTools {
 
-    public static void main(String[] argv) {
+    static String version = "1.5.10";
 
-        IGVConstants.setHeadless(true);
+    static String[] commandDocs = new String[]{
+            "version print the version number",
+            "sort    sort an alignment file by start position",
+            "index   index an alignment file",
+            "tile    convert an input file (cn, gct, wig) to tiled data format (tdf)",
+            "count   compute coverage density for an alignment file"
+    };
+    static final int MAX_RECORDS_IN_RAM = 500000;
+    static final int MAX_ZOOM = 7;
+    static final int WINDOW_SIZE = 25;
+    static final int EXT_FACTOR = 0;
+    static final Object PROBE_FILE = null;
+    static final int LINEAR_BIN_SIZE = 16000;
+    static final int INTERVAL_SIZE = 1000;
+    static final int LINEAR_INDEX = 1;
+    static final int INTERVAL_INDEX = 2;
+
+    /**
+     * The general usage string
+     */
+    static String usageString() {
+        StringBuffer buf = new StringBuffer();
 
+        buf.append("\nProgram: igvtools\n\n");
+        buf.append("Usage: igvtools [command] [options] [arguments]\n\n");
+        buf.append("Command:");
+        for (String c : commandDocs) {
+            buf.append(" " + c + "\n\t");
 
-        if (argv.length < 1) {
-            System.out.println("Args: " + argv.length);
-            System.out.println(usageString());
-            System.exit(-1);
         }
+        return buf.toString();
+    }
+
+
+    public static void main(String[] argv) throws IOException, PreprocessingException {
+
+        Globals.setHeadless(true);
 
         CmdLineParser parser = new CmdLineParser();
-        CmdLineParser.Option maxZoom = parser.addIntegerOption('z', "maxZoom");
-        CmdLineParser.Option windowSize = parser.addIntegerOption('w', "windowSize");
-        CmdLineParser.Option extFactor = parser.addIntegerOption('e', "ext");
+        CmdLineParser.Option helpOption = parser.addBooleanOption('h', "help");
+        CmdLineParser.Option guiOption = parser.addBooleanOption('g', "gui");
         CmdLineParser.Option windowFunctions = parser.addStringOption('f', "windowFunctions");
         CmdLineParser.Option tmpDirOption = parser.addStringOption('t', "tmpDir");
         CmdLineParser.Option probeFileOption = parser.addStringOption('p', "probeFile");
-
         CmdLineParser.Option maxZoomOption = parser.addIntegerOption('z', "maxZoom");
+        CmdLineParser.Option compressOption = parser.addBooleanOption('c', "compressed");
+        CmdLineParser.Option maxRecordsOption = parser.addIntegerOption('m', "maxRecords");
+
+        // options for coverage counter
         CmdLineParser.Option windowSizeOption = parser.addIntegerOption('w', "windowSize");
-        CmdLineParser.Option extFactorOption = parser.addIntegerOption('e', "ext");
-        CmdLineParser.Option gzippedOption = parser.addBooleanOption('c', "compressed");
+        CmdLineParser.Option extFactorOption = parser.addIntegerOption('e', "extFactor");
 
+        // options for indexing
+        CmdLineParser.Option indexTypeOption = parser.addIntegerOption('i', "indexType");
+        CmdLineParser.Option binSizeOption = parser.addIntegerOption('b', "binSize");
 
         // options for gentest
         CmdLineParser.Option nRows = parser.addIntegerOption('r', "numRows");
-        CmdLineParser.Option nSamples = parser.addIntegerOption('s', "numSamples");
+        CmdLineParser.Option strandOption = parser.addIntegerOption('s', "strand");
         CmdLineParser.Option unOrdered = parser.addBooleanOption('u', "unOrdered");
 
 
-        // Parse optional arguments
+        // Parse optional arguments (switches, etc)
         try {
             parser.parse(argv);
         } catch (CmdLineParser.OptionException e) {
             System.err.println(e.getMessage());
-            //printUsage();
-            return;
-        }
-        String[] nonOptionArgs = parser.getRemainingArgs();
-
-        String command = nonOptionArgs[0];
-        if (command.equals("version")) {
-            System.out.println("Version " + AboutDialog.VERSION + " build " + AboutDialog.BUILD +
-                    "   " + AboutDialog.TIMESTAMP);
             return;
         }
 
-        if (nonOptionArgs.length < 2) {
+        boolean help = ((Boolean) parser.getOptionValue(helpOption, false));
+        if (argv.length < 1 || help) {
             System.out.println(usageString());
             return;
         }
 
-        String ifile = nonOptionArgs[1];
-        String ext = Preprocessor.getExtension(ifile);
-        String genomeId = null;
-
-        if (command.toLowerCase().equals("pre") ||
-                command.toLowerCase().equals("tile") ||
-                command.toLowerCase().equals("count")) {
 
+        boolean gui = ((Boolean) parser.getOptionValue(guiOption, false));
+        if (gui) {
+            launchGUI();
+            Runtime.getRuntime().halt(0);
+        }
 
-            if (nonOptionArgs.length < 3) {
-                Preprocessor.printUsage();
-                return;
-            }
 
+        int maxZoomValue = (Integer) parser.getOptionValue(maxZoomOption, MAX_ZOOM);
+        int windowSizeValue = (Integer) parser.getOptionValue(windowSizeOption, WINDOW_SIZE);
+        int extFactorValue = (Integer) parser.getOptionValue(extFactorOption, EXT_FACTOR);
+        String wfsString = (String) parser.getOptionValue(windowFunctions);
+        String probeFile = (String) parser.getOptionValue(probeFileOption, PROBE_FILE);
+        String tmpDirName = (String) parser.getOptionValue(tmpDirOption);
+        int maxRecords = (Integer) parser.getOptionValue(maxRecordsOption, MAX_RECORDS_IN_RAM);
+        int strandOptionValue = (Integer) parser.getOptionValue(strandOption, -1);
 
-            File inputFile = new File(ifile);
-            if (!inputFile.exists()) {
-                System.err.println("File not found: " + inputFile.getAbsolutePath());
-                System.exit(-1);
-            }
+        int indexType = (Integer) parser.getOptionValue(indexTypeOption, LINEAR_INDEX);
+        int defaultBinSize = indexType == LINEAR_INDEX ? LINEAR_BIN_SIZE : INTERVAL_SIZE;
+        int binSize = (Integer) parser.getOptionValue(binSizeOption, defaultBinSize);
 
-            String ofile = nonOptionArgs[2];
+        int nRowsValue = (Integer) parser.getOptionValue(nRows, 10);
+        boolean sorted = !((Boolean) parser.getOptionValue(unOrdered, false));
 
-            if (nonOptionArgs.length > 3) {
-                String genomeFilePrefix = nonOptionArgs[3];
+        String[] nonOptionArgs = parser.getRemainingArgs();
 
-                String rootDir = FileUtils.getInstallDirectory();
+        try {
+            validateArgsLength(nonOptionArgs, 1);
 
-                File genomeFile = new File(rootDir, "genomes" + File.separator + genomeFilePrefix + ".genome");
-                System.out.println(genomeFile.getAbsolutePath());
-                if (!genomeFile.exists()) {
-                    genomeFile = new File(rootDir, "genomes" + File.separator + genomeFilePrefix);
-                }
-                if (!genomeFile.exists()) {
-                    genomeFile = new File(genomeFilePrefix);
-                }
-                if (!genomeFile.exists()) {
-                    System.err.println("Genome definition file not found for: " + genomeFilePrefix);
-                    System.exit(-1);
-                }
+            // The command
+            String command = nonOptionArgs[EXT_FACTOR].toLowerCase();
 
-                GenomeManager.GenomeListItem item = GenomeManager.getInstance().loadGenomeFromLocalFile(genomeFile);
-                genomeId = item.getId();
-                IGVModel.getInstance().getViewContext().setGenomeIdHeadless(genomeId);
+            // Do "version" now, its the only command with no arguments
+            if (command.equals("version")) {
+                System.out.println("Version " + version);
+                return;
             }
 
-            Integer maxZoomValue = (Integer) parser.getOptionValue(maxZoomOption, 7);
-            Integer windowSizeValue = (Integer) parser.getOptionValue(windowSizeOption, 25);
-            Integer extFactorValue = (Integer) parser.getOptionValue(extFactorOption, 0);
-            String wfsString = (String) parser.getOptionValue(windowFunctions);
-            String probeFile = (String) parser.getOptionValue(probeFileOption, null);
-            Boolean gzippedValue = (Boolean) parser.getOptionValue(gzippedOption);
-            boolean gzipped = gzippedValue != null && gzippedValue.booleanValue();
-
-            if (ext.equals(".txt")) {
-                ifile = ifile.substring(0, ifile.length() - 4);
+            // All remaining commands require an input file, and most need the file extension.  Do that here.
+            validateArgsLength(nonOptionArgs, 2);
+            String ifile = nonOptionArgs[1];
+            if (!(new File(ifile)).exists()) {
+                throw new PreprocessingException("File not found: " + ifile);
             }
-            if (ext.equals(".cbs") || ext.equals(".seg")) {
-                doPreprocessSeg(argv);
-            } else if (ext.equals(".cn") ||
-                    ext.equals(".wig") ||
-                    ext.equals(".ewig") ||
-                    ext.equals(".igv") ||
-                    ext.equals(".snp") ||
-                    ext.equals(".xcn") ||
-                    ext.equals(".gct") ||
-                    Preprocessor.isAlignmentFile(ext)) {
 
-                try {
-                    doPreprocess(command, ifile, ofile, probeFile, genomeId, wfsString, maxZoomValue, windowSizeValue,
-                            extFactorValue, gzipped);
-                } catch (IOException e) {
-                    System.out.println("Error preprocessing: " + ifile);
-                    e.printStackTrace();
-                    return;
+            if (command.equals("count") || command.equals("pre") || command.equals("tile")) {
+                validateArgsLength(nonOptionArgs, 4);
+                String ofile = nonOptionArgs[2];
+                // Output files must have .tdf extension
+                //if (!ofile.endsWith(".tdf")) {
+                //    ofile = ofile + ".tdf";
+                //}
+                String genomeId = nonOptionArgs[3];
+                boolean isGCT = Preprocessor.getExtension(ifile).endsWith("gct");
+                Collection<WindowFunction> wfList = parseWFS(wfsString, isGCT);
+
+                if (command.equals("count")) {
+                    doCount(ifile, ofile, genomeId, maxZoomValue, wfList, windowSizeValue, extFactorValue, strandOptionValue);
+                } else {
+                    doTile(ifile, ofile, probeFile, genomeId, maxZoomValue, wfList);
                 }
-            } else {
-                System.out.println("Command " + command + " not supported for files of type: " + ext);
-            }
-        } else {
-            int nArgs = argv.length - 1;
-            String[] args = new String[nArgs];
-            System.arraycopy(argv, 1, args, 0, nArgs);
-            if (command.toLowerCase().equals("sort")) {
-                doSort(argv);
-            } else if (command.toLowerCase().equals("index")) {
-                doIndex(argv);
-            } else if (command.toLowerCase().equals("wibtowig")) {
-                UCSCUtils.main(args);
-
-            } else if (command.toLowerCase().equals("splitgff")) {
-                doSplitGFF(args);
-            } else if (command.toLowerCase().equals("gentest")) {
+            } else if (command.toLowerCase().equals("sort")) {
+                validateArgsLength(nonOptionArgs, 3);
+                String ofile = nonOptionArgs[2];
+                doSort(ifile, ofile, tmpDirName, maxRecords);
+            } else if (command.equals("index")) {
+                doIndex(ifile, indexType, binSize);
+            } else if (command.equals("seg")) {
+                doPreprocessSeg(argv);
+            } else if (command.equals("wibtowig")) {
+                validateArgsLength(nonOptionArgs, 4);
+                File txtFile = new File(nonOptionArgs[1]);
+                File wibFile = new File(nonOptionArgs[2]);
+                File wigFile = new File(nonOptionArgs[3]);
+                String trackLine = nonOptionArgs.length > 4 ? nonOptionArgs[4] : null;
+                doWIBtoWIG(txtFile, wibFile, wigFile, trackLine);
+            } else if (command.equals("splitgff")) {
+                validateArgsLength(nonOptionArgs, 3);
+                String outputDirectory = nonOptionArgs[2];
+                GFFParser.splitFileByType(ifile, outputDirectory);
+            } else if (command.equals("gentest")) {
+                validateArgsLength(nonOptionArgs, 2);
                 String ofile = ifile;
-                int nRowsValue = (Integer) parser.getOptionValue(nRows);
-                int nSamplesValue = (Integer) parser.getOptionValue(nSamples);
-                boolean sorted = parser.getOptionValue(unOrdered) == null;
-                TestFileGenerator.generateTestFile(ofile, sorted, nRowsValue, nSamplesValue);
-
+                TestFileGenerator.generateTestFile(ofile, sorted, nRowsValue, strandOptionValue);
+            }  else if (command.toLowerCase().equals("gcttoigv")) {
+                validateArgsLength(nonOptionArgs, 4);
+                String ofile = nonOptionArgs[2];
+                // Output files must have .igv extension
+                if (!ofile.endsWith(".igv")) {
+                    ofile = ofile + ".igv";
+                }
+                String genomeId = nonOptionArgs[3];
+                doGCTtoIGV(ifile, ofile, probeFile, genomeId);
             } else {
-                System.out.println("Unknown command: " + argv[0]);
-                System.out.println(usageString());
+                throw new PreprocessingException("Unknown command: " + argv[EXT_FACTOR]);
             }
+        } catch (PreprocessingException e) {
+            System.err.println(e.getMessage());
+        } catch (IOException e) {
+            throw new PreprocessingException("Unexpected IO error: ", e);
         }
+    }
 
+    private static void doGCTtoIGV(String ifile, String ofile, String probefile, String genomeId) throws IOException {
+        Genome genome = loadGenome(genomeId);
+        if (genome == null) {
+            throw new PreprocessingException("Genome could not be loaded: " + genomeId);
+        }
+        GCTtoIGVConverter.convert(new File(ifile), new File(ofile), probefile, genomeId);
+        
     }
 
-    static String usageString() {
-        StringBuffer buf = new StringBuffer();
+    public static void doTile(String ifile, String ofile, String probeFile, String genomeId, int maxZoomValue,
+                              Collection<WindowFunction> windowFunctions) throws IOException, PreprocessingException {
+        validateIsTilable(ifile);
 
-        buf.append("\nProgram: igvtools\n\n");
-        buf.append("Usage: igvtools [command] [options] [arguments]\n\n");
-        buf.append("Command:");
-        for (String c : commandDocs) {
-            buf.append(" " + c + "\n\t");
+        System.out.println("Tile.  File = " + ifile);
+        System.out.println("Max zoom = " + maxZoomValue);
+        if (probeFile != null && probeFile.trim().length() > 0) {
+            System.out.println("Probe file = " + probeFile);
+        }
+        System.out.print("Window functions: ");
+        for (WindowFunction wf : windowFunctions) {
+            System.out.print(wf.toString() + " ");
+        }
+        System.out.println();
 
+        Genome genome = loadGenome(genomeId);
+        if (genome == null) {
+            throw new PreprocessingException("Genome could not be loaded: " + genomeId);
         }
-        return buf.toString();
-    }
+        File tmp = new File(ifile);
 
-    static String[] commandDocs = new String[]{
-            "sort    sort an alignment file by start position",
-            "index   index an alignment file",
-            "tile    convert an igv input file (cn, gct, wig) to tiled data format",
-            "count   compute coverage density for an alignment file"
-    };
+        // TODO -- do a better size estimate for directories
+        int nLines = tmp.isDirectory() ? 310000000 : ParsingUtils.estimateLineCount(ifile);
 
-    private static void doPreprocessH5(String[] args) {
-        try {
-            IGVPreprocessor.main(args);
-        } catch (Exception ex) {
-            System.err.println("Error preprocessing: " + printArgs(args));
-            ex.printStackTrace();
+        Preprocessor p = new Preprocessor(new File(ofile), genome, windowFunctions, nLines, null);
+        if (tmp.isDirectory()) {
+            for (File f : tmp.listFiles()) {
+                p.preprocess(f, probeFile, maxZoomValue);
+            }
+        } else {
+            p.preprocess(tmp, probeFile, maxZoomValue);
         }
+        p.finish();
+
+        System.out.flush();
+
     }
 
-    private static void doPreprocessSeg(String[] argv) {
-        try {
-            int nArgs = argv.length - 1;
-            String[] args = new String[nArgs];
-            System.arraycopy(argv, 1, args, 0, nArgs);
-            SegmentedDataWriter.main(args);
-        } catch (Exception ex) {
-            System.err.println("Error preprocessing: " + printArgs(argv));
-            ex.printStackTrace();
+    public static void doCount(String ifile, String ofile, String genomeId, int maxZoomValue,
+                               Collection<WindowFunction> windowFunctions,
+                               int windowSizeValue, int extFactorValue, int strandOption) throws IOException {
+        System.out.println("Computing coverage.  File = " + ifile);
+        System.out.println("Max zoom = " + maxZoomValue);
+        System.out.println("Window size = " + windowSizeValue);
+        System.out.print("Window functions: ");
+        for (WindowFunction wf : windowFunctions) {
+            System.out.print(wf.toString() + " ");
         }
+        System.out.println();
+        System.out.println("Ext factor = " + extFactorValue);
 
-    }
 
-    private static void doIndex(String[] argv) {
-        if (argv.length < 2) {
-            System.out.println("Usage: igvtools index <alignmentFile>");
-            System.exit(-1);
+        Genome genome = loadGenome(genomeId);
+        if (genome == null) {
+            throw new PreprocessingException("Genome could not be loaded: " + genomeId);
+        }
+        int nLines = ParsingUtils.estimateLineCount(ifile);
+
+        // Multiple files allowed for count command (a tdf and a wig)
+        File tdfFile = null;
+        File wigFile = null;
+        String[] files = ofile.split(",");
+        if (files[0].endsWith("wig")) {
+            wigFile = new File(files[0]);
+        } else {
+            tdfFile = new File(files[0]);
         }
-        if (argv[1].endsWith(".gz")) {
+        if (files.length > 1) {
+            if (files[1].endsWith("wig")) {
+                wigFile = new File(files[1]);
+            } else if (files[1].endsWith("tdf")) {
+                tdfFile = new File(files[1]);
+            }
+        }
+
+        if (tdfFile != null && !tdfFile.getName().endsWith(".tdf")) {
+            tdfFile = new File(tdfFile.getAbsolutePath() + ".tdf");
+        }
+
+        Preprocessor p = new Preprocessor(tdfFile, genome, windowFunctions, nLines, null);
+        p.count(ifile, windowSizeValue, extFactorValue, maxZoomValue, wigFile, strandOption);
+        p.finish();
+
+        System.out.flush();
+    }
+
+
+    public static void doWIBtoWIG(File txtFile, File wibFile, File wigFile, String trackLine) {
+        UCSCUtils.convertWIBFile(txtFile, wibFile, wigFile, trackLine);
+    }
+
+
+    public static void doPreprocessSeg(String[] argv) {
+        int nArgs = argv.length - 1;
+        String[] args = new String[nArgs];
+        System.arraycopy(argv, 1, args, EXT_FACTOR, nArgs);
+        SegmentedDataWriter.main(args);
+    }
+
+
+    /**
+     * Create an index for an alignment or feature file
+     *
+     * @param ifile
+     * @throws IOException
+     */
+    public static void doIndex(String ifile, int indexType, int binSize) throws IOException {
+        if (ifile.endsWith(".gz")) {
             System.out.println("Cannot index a gzipped file");
-            System.exit(-1);
+            throw new PreprocessingException("Cannot index a gzipped file");
         }
 
-        File inputFile = new File(argv[1]);
-        AlignmentIndexer indexer = AlignmentIndexer.getInstance(inputFile, null, null);
-        try {
+        File inputFile = new File(ifile);
+        if (ifile.endsWith(".bed") || ifile.endsWith(".vcf") || ifile.endsWith(".gff") ||
+                ifile.endsWith(".gff3") || ifile.endsWith(".psl") || ifile.endsWith(".pslx")) {
+            FeatureCodec codec = CodecFactory.getCodec(ifile);
+
+            IndexCreator indexer = null;
+            if (indexType == LINEAR_INDEX) {
+                LinearIndexCreator c = new LinearIndexCreator(inputFile, codec);
+                c.setBinWidth(binSize);
+                indexer = c;
+            } else {
+                IntervalIndexCreator c = new IntervalIndexCreator(inputFile, codec);
+                c.setFeaturesPerInterval(binSize);
+                indexer = c;
+            }
+
+            indexer.createIndex();
+
+        } else {
+            AlignmentIndexer indexer = AlignmentIndexer.getInstance(inputFile, null, null);
             indexer.createSamIndex();
-        } catch (Exception ex) {
-            ex.printStackTrace();
-            System.exit(-1);
         }
-    }
+        System.out.flush();
 
-    private static void doSort(String[] argv) {
-        String[] args = new String[argv.length - 1];
-        System.arraycopy(argv, 1, args, 0, argv.length - 1);
-        Sorter.getSorter(args).run();
     }
 
-    private static void doPreprocess(String command,
-                                     String iFile,
-                                     String oFile,
-                                     String probeFile,
-                                     String genomeId,
-                                     String wfsString,
-                                     int maxZoomValue,
-                                     int windowSizeValue,
-                                     int extFactorValue,
-                                     boolean gzipped) throws IOException {
 
+    public static void doSort(String ifile, String ofile, String tmpDirName, int maxRecords) {
 
-        if (!(new File(iFile)).exists()) {
-            System.out.println("File not found: " + iFile);
-            return;
+        System.out.println("Sorting " + ifile);
+        File inputFile = new File(ifile);
+        File outputFile = new File(ofile);
+        Sorter sorter = Sorter.getSorter(inputFile, outputFile);
+        if (tmpDirName != null && tmpDirName.trim().length() > 0) {
+            File tmpDir = new File(tmpDirName);
+            if (!tmpDir.exists()) {
+                System.err.println("Error: tmp directory: " + tmpDir.getAbsolutePath() + " does not exist.");
+                throw new PreprocessingException("Error: tmp directory: " + tmpDir.getAbsolutePath() + " does not exist.");
+            }
+            sorter.setTmpDir(tmpDir);
+        }
+
+        sorter.setMaxRecords(maxRecords);
+        sorter.run();
+        System.out.println("Done");
+        System.out.flush();
+    }
+
+
+    private static void validateArgsLength(String[] nonOptionArgs, int len) throws PreprocessingException {
+        if (nonOptionArgs.length < len) {
+            throw new PreprocessingException(usageString());
         }
+    }
+
 
+    public static Genome loadGenome(String genomeFileOrID) {
 
-        // Output files must have .tdf extension
-        if (!oFile.endsWith(".tdf")) {
-            oFile = oFile + ".tdf";
+
+        String rootDir = FileUtils.getInstallDirectory();
+
+        Genome genome = GenomeManager.getInstance().getGenome(genomeFileOrID);
+        if (genome != null) {
+            return genome;
         }
 
-        int nLines = ParsingUtils.estimateLineCount(iFile);
 
-        Preprocessor p = new Preprocessor(new File(oFile), genomeId, wfsString,
-                new CommandLineStatusMonitor(), nLines, gzipped);
-        p.preprocess(iFile, oFile, probeFile, command, maxZoomValue, windowSizeValue, extFactorValue);
+        File genomeFile = new File(rootDir, "genomes" + File.separator + genomeFileOrID + ".genome");
+        if (!genomeFile.exists()) {
+            genomeFile = new File(rootDir, "genomes" + File.separator + genomeFileOrID);
+        }
+        if (!genomeFile.exists()) {
+            genomeFile = new File(genomeFileOrID);
+        }
+        if (!genomeFile.exists()) {
+            throw new PreprocessingException("Genome definition file not found for: " + genomeFileOrID);
+        }
+
+        GenomeManager.GenomeListItem item = GenomeManager.getInstance().loadGenomeFromLocalFile(genomeFile);
+        String genomeId = item.getId();
+        if (genomeId == null) {
+            throw new PreprocessingException("Error loading: " + genomeFileOrID);
+        }
+        ViewContext.getInstance().setGenomeId(genomeId);
+        genome = GenomeManager.getInstance().getGenome(genomeId);
+        return genome;
     }
 
-    private static void doSplitGFF(String[] args) {
-        try {
-            GFFParser.main(args);
-        } catch (Exception e) {
-            System.err.println("Error preprocessing: " + printArgs(args));
-            e.printStackTrace();
+
+    /**
+     * Test if the file type can be "tiled".
+     */
+    private static void validateIsTilable(String ifile) {
+        File tmp = new File(ifile);
+        if (tmp.isDirectory()) {
+            File[] files = tmp.listFiles();
+            if (files.length == EXT_FACTOR) {
+                throw new PreprocessingException("Tile command not supported for empty directory: " + ifile);
+            }
+            ifile = files[EXT_FACTOR].getName();
+        }
+        String ext = Preprocessor.getExtension(ifile);
+        if (!(ext.equals(".cn") ||
+                ext.equals(".igv") ||
+                ext.equals(".wig") ||
+                ext.equals(".ewig") ||
+                ext.equals(".cn") ||
+                ext.equals(".snp") ||
+                ext.equals(".xcn") ||
+                ext.equals(".gct") ||
+                ext.equals(".bedgraph") ||
+                Preprocessor.isAlignmentFile(ext))) {
+            throw new PreprocessingException("Tile command not supported for files of type: " + ext);
         }
     }
 
-    private static String printArgs(String[] args) {
-        StringBuffer buf = new StringBuffer(100);
-        for (int i = 0; i < args.length; i++) {
-            buf.append(args[i]);
+
+    /**
+     * Parse the window functions line.
+     *
+     * @param string comma delimited string of window functions, e.g. min, p10, max
+     * @return colleciton of WindowFunctions objects
+     */
+    private static Collection<WindowFunction> parseWFS(String string, boolean isGCT) {
+        if (string == null || string.length() == EXT_FACTOR) {
+            return isGCT ? Arrays.asList(WindowFunction.min, WindowFunction.mean, WindowFunction.max) :
+                    Arrays.asList(WindowFunction.mean);
+        } else {
+            String[] tokens = string.split(",");
+            List<WindowFunction> funcs = new ArrayList(tokens.length);
+            for (int i = EXT_FACTOR; i < tokens.length; i++) {
+                String wf = tokens[i];
+                if (wf.startsWith("p")) {
+                    wf = wf.replaceFirst("p", "percentile");
+                }
+                try {
+                    funcs.add(WindowFunction.valueOf(wf));
+                } catch (Exception e) {
+                    System.err.println("Unrecognized window function: " + tokens[i]);
+                }
+            }
+            return funcs;
         }
-        return buf.toString();
     }
+
+
+    private static void launchGUI() {
+        IgvToolsGui.main(null);
+    }
+
 }
+
diff --git a/src/org/broad/igv/tools/IgvToolsGui.java b/src/org/broad/igv/tools/IgvToolsGui.java
new file mode 100644
index 0000000..9ef61d5
--- /dev/null
+++ b/src/org/broad/igv/tools/IgvToolsGui.java
@@ -0,0 +1,1002 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+package org.broad.igv.tools;
+
+import javax.swing.border.*;
+
+import com.jidesoft.utils.SwingWorker;
+import org.broad.igv.track.WindowFunction;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class IgvToolsGui extends JDialog {
+
+    static JFileChooser fileDialog;
+
+    private static final String COUNT = "Count";
+    private static final String SORT = "Sort";
+    private static final String INDEX = "Index";
+    private static final String TILE = "Tile";
+
+    String[] tools = {TILE, COUNT, SORT, INDEX};
+    String[] zoomLevels = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"};
+
+    PrintStream systemOutStream;
+    PrintStream systemErrStream;
+
+    public IgvToolsGui() {
+
+        initComponents();
+        initUI();
+        updateUI();
+
+        closeButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                close();
+                setVisible(false);
+            }
+        });
+        inputButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                try {
+                    File chosenFile = chooseFile();
+                    inputField.setText(chosenFile.getAbsolutePath());
+                    updateUI();
+                } catch (NullPointerException npe) {
+                }
+            }
+        });
+        outputButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                try {
+                    File chosenFile = chooseFile();
+                    outputField.setText(chosenFile.getAbsolutePath());
+                    updateUI();
+                } catch (NullPointerException npe) {
+                }
+            }
+        });
+        genomeButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                try {
+                    File chosenFile = chooseFile();
+                    genomeField.setText(chosenFile.getAbsolutePath());
+                    updateUI();
+                } catch (NullPointerException npe) {
+                }
+            }
+        });
+        probeButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                try {
+                    File chosenFile = chooseFile();
+                    probeField.setText(chosenFile.getAbsolutePath());
+                    updateUI();
+                } catch (NullPointerException npe) {
+                }
+            }
+        });
+        toolCombo.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                updateUI();
+            }
+        });
+        runButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                run();
+            }
+        });
+
+        tempButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                try {
+                    File chosenFile = chooseFile();
+                    tempField.setText(chosenFile.getAbsolutePath());
+                    updateUI();
+                } catch (NullPointerException npe) {
+                }
+            }
+        });
+    }
+
+    private void initUI() {
+        setContentPane(mainPanel);
+        setModal(true);
+
+        addWindowListener(new WindowAdapter() {
+            public void windowClosing(WindowEvent windowEvent) {
+                close();
+            }
+        });
+
+        for (String item : zoomLevels) {
+            zoomCombo.addItem(item);
+        }
+        for (String tool : tools) {
+            toolCombo.addItem(tool);
+        }
+
+        zoomCombo.setSelectedIndex(IgvTools.MAX_ZOOM);
+        windowField.setText(String.valueOf(IgvTools.WINDOW_SIZE));
+        recordField.setText(String.valueOf(IgvTools.MAX_RECORDS_IN_RAM));
+        redirectSystemStreams();
+    }
+
+    private void close() {
+        System.setErr(systemErrStream);
+        System.setOut(systemOutStream);
+        dispose();
+    }
+
+    private void updateTextArea(final String text) {
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                outputText.append(text);
+            }
+        });
+    }
+
+    private void redirectSystemStreams() {
+        OutputStream out = new OutputStream() {
+            @Override
+            public void write(int b) throws IOException {
+                updateTextArea(String.valueOf((char) b));
+            }
+
+            @Override
+            public void write(byte[] b, int off, int len) throws IOException {
+                updateTextArea(new String(b, off, len));
+            }
+
+            @Override
+            public void write(byte[] b) throws IOException {
+                write(b, 0, b.length);
+            }
+        };
+
+        systemOutStream = System.out;
+        systemErrStream = System.err;
+
+        System.setOut(new PrintStream(out, true));
+        System.setErr(new PrintStream(out, true));
+    }
+
+
+    private void updateUI() {
+        String tool = (String) toolCombo.getSelectedItem();
+
+        if (tool.equals(COUNT)) {
+            inputField.setEnabled(true);
+            inputButton.setEnabled(true);
+            outputField.setEnabled(true);
+            outputButton.setEnabled(true);
+            outputLabel.setEnabled(true);
+
+            if (!this.genomeSelectionDisabled) {
+                genomeField.setEnabled(true);
+                genomeButton.setEnabled(true);
+                genomeLabel.setEnabled(true);
+            }
+
+            zoomCombo.setEnabled(true);
+            zoomLabel.setEnabled(true);
+            recordField.setEnabled(false);
+            recordLabel.setEnabled(false);
+            probeField.setEnabled(false);
+            probeButton.setEnabled(false);
+            probeLabel.setEnabled(false);
+            tempLabel.setEnabled(false);
+            tempField.setEnabled(false);
+            tempButton.setEnabled(false);
+            windowLabel.setEnabled(true);
+            windowField.setEnabled(true);
+            enableWindowFunctions();
+
+        } else if (tool.equals(SORT)) {
+            inputField.setEnabled(true);
+            inputButton.setEnabled(true);
+            outputField.setEnabled(true);
+            outputButton.setEnabled(true);
+            outputLabel.setEnabled(true);
+            genomeField.setEnabled(false);
+            genomeButton.setEnabled(false);
+            genomeLabel.setEnabled(false);
+            zoomCombo.setEnabled(false);
+            zoomLabel.setEnabled(false);
+            recordField.setEnabled(true);
+            recordLabel.setEnabled(true);
+            probeField.setEnabled(false);
+            probeButton.setEnabled(false);
+            probeLabel.setEnabled(false);
+            tempLabel.setEnabled(true);
+            tempField.setEnabled(true);
+            tempButton.setEnabled(true);
+            windowLabel.setEnabled(false);
+            windowField.setEnabled(false);
+            disableWindowFunctions();
+
+        } else if (tool.equals(INDEX)) {
+            inputField.setEnabled(true);
+            inputButton.setEnabled(true);
+            outputField.setEnabled(false);
+            outputButton.setEnabled(false);
+            outputLabel.setEnabled(false);
+            genomeField.setEnabled(false);
+            genomeButton.setEnabled(false);
+            genomeLabel.setEnabled(false);
+            zoomCombo.setEnabled(false);
+            zoomLabel.setEnabled(false);
+            recordField.setEnabled(false);
+            recordLabel.setEnabled(false);
+            probeField.setEnabled(false);
+            probeButton.setEnabled(false);
+            probeLabel.setEnabled(false);
+            tempLabel.setEnabled(false);
+            tempField.setEnabled(false);
+            tempButton.setEnabled(false);
+            windowLabel.setEnabled(false);
+            windowField.setEnabled(false);
+            disableWindowFunctions();
+
+        } else if (tool.equals(TILE)) {
+            inputField.setEnabled(true);
+            inputButton.setEnabled(true);
+            outputField.setEnabled(true);
+            outputButton.setEnabled(true);
+            outputLabel.setEnabled(true);
+
+            if (!this.genomeSelectionDisabled) {
+                genomeField.setEnabled(true);
+                genomeButton.setEnabled(true);
+                genomeLabel.setEnabled(true);
+            }
+
+            zoomCombo.setEnabled(true);
+            zoomLabel.setEnabled(true);
+            recordField.setEnabled(false);
+            recordLabel.setEnabled(false);
+            probeField.setEnabled(true);
+            probeButton.setEnabled(true);
+            probeLabel.setEnabled(true);
+            tempLabel.setEnabled(false);
+            tempField.setEnabled(false);
+            tempButton.setEnabled(false);
+            windowLabel.setEnabled(false);
+            windowField.setEnabled(false);
+            enableWindowFunctions();
+        }
+    }
+
+    private void enableWindowFunctions() {
+        Component[] com = windowFunctionPanel.getComponents();
+        windowFunctionLabel.setEnabled(true);
+        for (int i = 0; i < com.length; i++) {
+            com[i].setEnabled(true);
+        }
+    }
+
+    private void disableWindowFunctions() {
+        Component[] com = windowFunctionPanel.getComponents();
+        windowFunctionLabel.setEnabled(false);
+        for (int i = 0; i < com.length; i++) {
+            com[i].setEnabled(false);
+        }
+    }
+
+
+    private boolean validateFields() {
+        String tool = (String) toolCombo.getSelectedItem();
+
+        // All commands require in input file
+        if (inputField.getText().trim().length() == 0) {
+            showMessage("Input file is required");
+            return false;
+        }
+
+        if (tool.equals(INDEX)) {
+            return true;
+        }
+
+        // All commands except "index" require an output file
+        if (outputField.getText().trim().length() == 0) {
+            showMessage("Output file is required");
+            return false;
+        }
+
+        // See if file exists
+        if ((new File(outputField.getText().trim()).exists())) {
+            int opt = JOptionPane.showConfirmDialog(this, "Output file: " + outputField.getText() + " exists.  Overwite?");
+            if (opt != JOptionPane.YES_OPTION) {
+                return false;
+            }
+        }
+
+        if (tool.equals(SORT)) {
+            return true;
+        }
+
+        if (genomeField.getText().trim().length() == 0) {
+            showMessage("Genome is required");
+            return false;
+        }
+
+        return true;
+    }
+
+    private void run() {
+        String tool = (String) toolCombo.getSelectedItem();
+        if (!validateFields()) {
+            return;
+        }
+        try {
+            if (tool.equals(COUNT)) {
+                doCount();
+            } else if (tool.equals(SORT)) {
+                doSort();
+            } else if (tool.equals(INDEX)) {
+                doIndex();
+            } else if (tool.equals(TILE)) {
+                doTile();
+            }
+        } catch (PreprocessingException e) {
+            showMessage("Error performing " + tool + ": " + e.getMessage());
+        }
+    }
+
+    private void showMessage(String tool) {
+        JOptionPane.showMessageDialog(this, tool);
+    }
+
+    private void doSort() {
+        // Sort is not interruptible, and there is no provision for progress.  So use a thread & wait cursor
+
+        runButton.setEnabled(false);
+        SwingWorker swingWorker = new SwingWorker() {
+
+            @Override
+            protected Object doInBackground() {
+                try {
+                    setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+                    String maxRecordText = recordField.getText();
+                    int maxRecords = (maxRecordText != null && maxRecordText.length() > 0) ?
+                            Integer.parseInt(maxRecordText) : IgvTools.MAX_RECORDS_IN_RAM;
+                    IgvTools.doSort(inputField.getText(), outputField.getText(), tempField.getText(), maxRecords);
+                } catch (Exception e) {
+                    showMessage("Error: " + e.getMessage());
+                }
+
+                return null;
+            }
+
+            @Override
+            protected void done() {
+                runButton.setEnabled(true);
+                setCursor(Cursor.getDefaultCursor());
+            }
+        };
+
+        swingWorker.execute();
+    }
+
+
+    /*
+    public static void doCount(String ifile, String ofile, String genomeId, int maxZoomValue, String wfsString,
+                               int windowSizeValue, int extFactorValue, int strandOption) throws IOException {
+
+     */
+    private void doCount() {
+
+        SwingWorker swingWorker = new SwingWorker() {
+
+            @Override
+            protected Object doInBackground() {
+                try {
+                    setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+                    String ifile = inputField.getText();
+                    String ofile = outputField.getText();
+                    String genomeId = genomeField.getText();
+                    int maxZoomValue = Integer.parseInt(zoomCombo.getSelectedItem().toString());
+                    Collection<WindowFunction> wfs = getWindowFunctions();
+
+                    String windowSizeText = windowField.getText();
+                    int windowSize = (windowSizeText != null && windowSizeText.length() > 0) ?
+                            Integer.parseInt(windowSizeText) : IgvTools.WINDOW_SIZE;
+
+                    int extFactor = 0;
+                    int strandOption = -1;
+
+                    runButton.setEnabled(false);
+                    IgvTools.doCount(ifile, ofile, genomeId, maxZoomValue, wfs, windowSize, extFactor, strandOption);
+                } catch (Exception e) {
+                    showMessage("Error: " + e.getMessage());
+                }
+
+                return null;
+            }
+
+            @Override
+            protected void done() {
+                runButton.setEnabled(true);
+                setCursor(Cursor.getDefaultCursor());
+            }
+        };
+
+        swingWorker.execute();
+    }
+
+    private void doTile() {
+
+        SwingWorker swingWorker = new SwingWorker() {
+
+            @Override
+            protected Object doInBackground() {
+                try {
+                    setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+                    String ifile = inputField.getText();
+                    String ofile = outputField.getText();
+                    String genomeId = genomeField.getText();
+                    int maxZoomValue = Integer.parseInt(zoomCombo.getSelectedItem().toString());
+                    Collection<WindowFunction> wfs = getWindowFunctions();
+                    String probeFile = probeField.getText();
+
+                    runButton.setEnabled(false);
+                    IgvTools.doTile(ifile, ofile, probeFile, genomeId, maxZoomValue, wfs);
+                } catch (Exception e) {
+                    showMessage("Error: " + e.getMessage());
+                }
+
+                return null;
+            }
+
+            @Override
+            protected void done() {
+                runButton.setEnabled(true);
+                setCursor(Cursor.getDefaultCursor());
+            }
+        };
+
+        swingWorker.execute();
+    }
+
+    //    public static void doIndex(String ifile, int indexType, int binSize) throws IOException {
+
+    private void doIndex() {
+
+        SwingWorker swingWorker = new SwingWorker() {
+
+            @Override
+            protected Object doInBackground() {
+                try {
+                    setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+                    String ifile = inputField.getText();
+                    int indexType = IgvTools.LINEAR_INDEX;
+                    int binSize = IgvTools.LINEAR_BIN_SIZE;
+
+                    runButton.setEnabled(false);
+                    IgvTools.doIndex(ifile, indexType, binSize);
+                } catch (Exception e) {
+                    showMessage("Error: " + e.getMessage());
+                }
+
+                return null;
+            }
+
+            @Override
+            protected void done() {
+                runButton.setEnabled(true);
+                setCursor(Cursor.getDefaultCursor());
+            }
+        };
+
+        swingWorker.execute();
+    }
+
+    private Collection<WindowFunction> getWindowFunctions() {
+        ArrayList<WindowFunction> wfs = new ArrayList();
+
+        if (minCheckBox.isSelected()) {
+            wfs.add(WindowFunction.min);
+        }
+        if (maxCheckBox.isSelected()) {
+            wfs.add(WindowFunction.max);
+        }
+        if (meanCheckBox.isSelected()) {
+            wfs.add(WindowFunction.mean);
+        }
+        if (a98CheckBox.isSelected()) {
+            wfs.add(WindowFunction.percentile98);
+        }
+        if (a90CheckBox.isSelected()) {
+            wfs.add(WindowFunction.percentile90);
+        }
+        if (a10CheckBox.isSelected()) {
+            wfs.add(WindowFunction.percentile10);
+        }
+        if (a2CheckBox.isSelected()) {
+            wfs.add(WindowFunction.percentile2);
+        }
+
+        if (wfs.isEmpty()) {
+            wfs.add(WindowFunction.mean);
+        }
+
+        return wfs;
+    }
+
+
+    private File chooseFile() {
+
+        //TODO Override so you can specify the file type with a string array ex: {".wig", ".tdf"}
+
+        if (fileDialog == null) {
+            fileDialog = new JFileChooser();
+        }
+
+        fileDialog.setMultiSelectionEnabled(false);
+        fileDialog.setFileSelectionMode(JFileChooser.FILES_ONLY);
+
+        int returnVal = fileDialog.showDialog(this, "Select File");
+        if (returnVal == JFileChooser.CANCEL_OPTION) {
+            return null;
+        } else {
+            File selected = fileDialog.getSelectedFile();
+            return selected;
+        }
+    }
+
+    public static void main(String[] args) {
+        launch(true, null);
+    }
+
+    private boolean genomeSelectionDisabled = false;
+
+    public static void launch(boolean modal, String genomeId) {
+        IgvToolsGui mainWindow = new IgvToolsGui();
+        mainWindow.pack();
+        mainWindow.setModal(modal);
+        mainWindow.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+        mainWindow.setResizable(false);
+
+        if (genomeId != null) {
+            mainWindow.genomeField.setText(genomeId);
+            mainWindow.genomeField.setEnabled(false);
+            mainWindow.genomeField.setToolTipText("<html>To change the genome id close this window and <br>use the pulldown on the IGV main screen.");
+            mainWindow.genomeButton.setEnabled(false);
+            mainWindow.genomeSelectionDisabled = true;
+        }
+
+        mainWindow.setVisible(true);
+    }
+
+
+    private void inputButtonActionPerformed(ActionEvent e) {
+        setDefaultOutputText();
+    }
+
+    private void inputFieldActionPerformed(ActionEvent e) {
+        setDefaultOutputText();
+    }
+
+    private void inputFieldFocusLost(FocusEvent e) {
+        setDefaultOutputText();
+    }
+
+    private void setDefaultOutputText() {
+        if (outputField.getText().length() == 0 && inputField.getText().length() > 0) {
+            String cmd = toolCombo.getSelectedItem().toString().toLowerCase();
+            if (cmd.equals("count") || cmd.equals("tile")) {
+                outputField.setText(inputField.getText() + ".tdf");
+            } else if (cmd.equals("sort")) {
+                String input = inputField.getText();
+                int ext = input.lastIndexOf(".");
+                if (ext > 0) {
+                    String output = input.substring(0, ext) + ".sorted" + input.substring(ext);
+                    outputField.setText(output);
+                }
+            }
+        }
+    }
+
+
+    private void initComponents() {
+        // JFormDesigner - Component initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
+        // Generated using JFormDesigner non-commercial license
+        mainPanel = new JPanel();
+        requiredPanel = new JPanel();
+        toolCombo = new JComboBox();
+        JLabel label1 = new JLabel();
+        JLabel label2 = new JLabel();
+        outputLabel = new JLabel();
+        outputButton = new JButton();
+        inputField = new JTextField();
+        inputButton = new JButton();
+        outputField = new JTextField();
+        genomeLabel = new JLabel();
+        genomeField = new JTextField();
+        genomeButton = new JButton();
+        tilePanel = new JPanel();
+        zoomLabel = new JLabel();
+        windowFunctionLabel = new JLabel();
+        windowFunctionPanel = new JPanel();
+        minCheckBox = new JCheckBox();
+        maxCheckBox = new JCheckBox();
+        meanCheckBox = new JCheckBox();
+        medianCheckBox = new JCheckBox();
+        a2CheckBox = new JCheckBox();
+        a10CheckBox = new JCheckBox();
+        a90CheckBox = new JCheckBox();
+        a98CheckBox = new JCheckBox();
+        probeLabel = new JLabel();
+        probeField = new JTextField();
+        probeButton = new JButton();
+        zoomCombo = new JComboBox();
+        windowLabel = new JLabel();
+        windowField = new JTextField();
+        sortPanel = new JPanel();
+        tempLabel = new JLabel();
+        recordLabel = new JLabel();
+        tempButton = new JButton();
+        tempField = new JTextField();
+        recordField = new JTextField();
+        buttonPanel = new JPanel();
+        runButton = new JButton();
+        closeButton = new JButton();
+        OutputPanel = new JPanel();
+        outputScroll = new JScrollPane();
+        outputText = new JTextArea();
+        JSeparator separator1 = new JSeparator();
+        progressBar = new JProgressBar();
+
+        //======== mainPanel ========
+        {
+            mainPanel.setBorder(new TitledBorder(new EtchedBorder(), ""));
+            mainPanel.setLayout(new GridBagLayout());
+
+            //======== requiredPanel ========
+            {
+                requiredPanel.setLayout(new GridBagLayout());
+                requiredPanel.add(toolCombo, new GridBagConstraints(2, 1, 1, 1, 1.0, 1.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+                    new Insets(0, 0, 0, 0), 0, 0));
+
+                //---- label1 ----
+                label1.setText("Command");
+                requiredPanel.add(label1, new GridBagConstraints(1, 1, 1, 1, 0.0, 1.0,
+                    GridBagConstraints.WEST, GridBagConstraints.NONE,
+                    new Insets(0, 0, 0, 0), 0, 0));
+
+                //---- label2 ----
+                label2.setText("Input File");
+                requiredPanel.add(label2, new GridBagConstraints(1, 2, 1, 1, 0.0, 1.0,
+                    GridBagConstraints.WEST, GridBagConstraints.NONE,
+                    new Insets(0, 0, 0, 0), 0, 0));
+
+                //---- outputLabel ----
+                outputLabel.setText("Output File");
+                requiredPanel.add(outputLabel, new GridBagConstraints(1, 3, 1, 1, 0.0, 1.0,
+                    GridBagConstraints.WEST, GridBagConstraints.NONE,
+                    new Insets(0, 0, 0, 0), 0, 0));
+
+                //---- outputButton ----
+                outputButton.setText("Browse");
+                requiredPanel.add(outputButton, new GridBagConstraints(3, 3, 1, 1, 0.0, 1.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+                    new Insets(0, 0, 0, 0), 0, 0));
+
+                //---- inputField ----
+                inputField.addFocusListener(new FocusAdapter() {
+                    @Override
+                    public void focusLost(FocusEvent e) {
+                        inputFieldFocusLost(e);
+                    }
+                });
+                inputField.addActionListener(new ActionListener() {
+                    public void actionPerformed(ActionEvent e) {
+                        inputFieldActionPerformed(e);
+                    }
+                });
+                requiredPanel.add(inputField, new GridBagConstraints(2, 2, 1, 1, 1.0, 1.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+                    new Insets(0, 0, 0, 0), 0, 0));
+
+                //---- inputButton ----
+                inputButton.setText("Browse");
+                inputButton.addActionListener(new ActionListener() {
+                    public void actionPerformed(ActionEvent e) {
+                        inputButtonActionPerformed(e);
+                    }
+                });
+                requiredPanel.add(inputButton, new GridBagConstraints(3, 2, 1, 1, 0.0, 1.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+                    new Insets(0, 0, 0, 0), 0, 0));
+                requiredPanel.add(outputField, new GridBagConstraints(2, 3, 1, 1, 1.0, 1.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+                    new Insets(0, 0, 0, 0), 0, 0));
+
+                //---- genomeLabel ----
+                genomeLabel.setToolTipText("Either a genome ID (e.g. hg18) or the full path to a .genome file.");
+                genomeLabel.setText("Genome");
+                requiredPanel.add(genomeLabel, new GridBagConstraints(1, 4, 1, 1, 0.0, 1.0,
+                    GridBagConstraints.WEST, GridBagConstraints.NONE,
+                    new Insets(0, 0, 0, 0), 0, 0));
+                requiredPanel.add(genomeField, new GridBagConstraints(2, 4, 1, 1, 1.0, 1.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+                    new Insets(0, 0, 0, 0), 0, 0));
+
+                //---- genomeButton ----
+                genomeButton.setText("Browse");
+                requiredPanel.add(genomeButton, new GridBagConstraints(3, 4, 1, 1, 0.0, 1.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+                    new Insets(0, 0, 0, 0), 0, 0));
+            }
+            mainPanel.add(requiredPanel, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0,
+                GridBagConstraints.CENTER, GridBagConstraints.BOTH,
+                new Insets(0, 0, 10, 0), 0, 0));
+
+            //======== tilePanel ========
+            {
+                tilePanel.setEnabled(true);
+                tilePanel.setFont(tilePanel.getFont().deriveFont(Font.ITALIC, 10f));
+                tilePanel.setBorder(new TitledBorder(null, "Tile and Count Options", TitledBorder.LEADING, TitledBorder.TOP));
+                tilePanel.setLayout(new GridBagLayout());
+
+                //---- zoomLabel ----
+                zoomLabel.setToolTipText("<html>Specifies the maximum zoom level to precompute. The default value is 7.<br>To reduce file size at the expense of Iperformance this value can be reduced.");
+                zoomLabel.setText("Zoom Levels");
+                tilePanel.add(zoomLabel, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0,
+                    GridBagConstraints.WEST, GridBagConstraints.NONE,
+                    new Insets(0, 0, 0, 0), 0, 0));
+
+                //---- windowFunctionLabel ----
+                windowFunctionLabel.setToolTipText("Window functions to use for summarizing data. ");
+                windowFunctionLabel.setText("Window Functions");
+                tilePanel.add(windowFunctionLabel, new GridBagConstraints(1, 2, 1, 1, 0.0, 0.0,
+                    GridBagConstraints.NORTHWEST, GridBagConstraints.NONE,
+                    new Insets(0, 0, 0, 0), 0, 0));
+
+                //======== windowFunctionPanel ========
+                {
+                    windowFunctionPanel.setLayout(new GridLayout(2, 0));
+
+                    //---- minCheckBox ----
+                    minCheckBox.setText("Min");
+                    windowFunctionPanel.add(minCheckBox);
+
+                    //---- maxCheckBox ----
+                    maxCheckBox.setText("Max");
+                    windowFunctionPanel.add(maxCheckBox);
+
+                    //---- meanCheckBox ----
+                    meanCheckBox.setSelected(true);
+                    meanCheckBox.setText("Mean");
+                    windowFunctionPanel.add(meanCheckBox);
+
+                    //---- medianCheckBox ----
+                    medianCheckBox.setText("Median");
+                    windowFunctionPanel.add(medianCheckBox);
+
+                    //---- a2CheckBox ----
+                    a2CheckBox.setText("2%");
+                    windowFunctionPanel.add(a2CheckBox);
+
+                    //---- a10CheckBox ----
+                    a10CheckBox.setText("10%");
+                    windowFunctionPanel.add(a10CheckBox);
+
+                    //---- a90CheckBox ----
+                    a90CheckBox.setText("90%");
+                    windowFunctionPanel.add(a90CheckBox);
+
+                    //---- a98CheckBox ----
+                    a98CheckBox.setText("98%");
+                    windowFunctionPanel.add(a98CheckBox);
+                }
+                tilePanel.add(windowFunctionPanel, new GridBagConstraints(2, 2, 1, 1, 1.0, 0.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.BOTH,
+                    new Insets(0, 0, 0, 0), 0, 0));
+
+                //---- probeLabel ----
+                probeLabel.setFont(probeLabel.getFont());
+                probeLabel.setToolTipText("<html>Specifies a \"bed\" file to be used to map probe identifiers to locations.  This option is useful <br>when preprocessing gct files.  The bed file should contain 4 columns: chr start end name\n<br>where name is the probe name in the gct file.");
+                probeLabel.setText("Probe to Loci Mapping");
+                tilePanel.add(probeLabel, new GridBagConstraints(1, 3, 1, 1, 0.0, 1.0,
+                    GridBagConstraints.WEST, GridBagConstraints.NONE,
+                    new Insets(0, 0, 0, 0), 0, 0));
+                tilePanel.add(probeField, new GridBagConstraints(2, 3, 1, 1, 1.0, 1.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+                    new Insets(0, 0, 0, 0), 0, 0));
+
+                //---- probeButton ----
+                probeButton.setText("Browse");
+                tilePanel.add(probeButton, new GridBagConstraints(3, 3, 1, 1, 0.0, 1.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+                    new Insets(0, 0, 0, 0), 0, 0));
+
+                //---- zoomCombo ----
+                zoomCombo.setEditable(false);
+                zoomCombo.setModel(new DefaultComboBoxModel(new String[] {
+
+                }));
+                tilePanel.add(zoomCombo, new GridBagConstraints(2, 1, 1, 1, 1.0, 0.0,
+                    GridBagConstraints.WEST, GridBagConstraints.NONE,
+                    new Insets(0, 0, 0, 0), 0, 0));
+
+                //---- windowLabel ----
+                windowLabel.setToolTipText("The window size over which coverage computed when using the count command.  Defaults to 25 bp.");
+                windowLabel.setText("Window Size");
+                tilePanel.add(windowLabel, new GridBagConstraints(1, 4, 1, 1, 0.0, 0.0,
+                    GridBagConstraints.WEST, GridBagConstraints.NONE,
+                    new Insets(0, 0, 0, 0), 0, 0));
+                tilePanel.add(windowField, new GridBagConstraints(2, 4, 1, 1, 1.0, 0.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+                    new Insets(0, 0, 0, 0), 0, 0));
+            }
+            mainPanel.add(tilePanel, new GridBagConstraints(1, 2, 1, 1, 1.0, 1.0,
+                GridBagConstraints.CENTER, GridBagConstraints.BOTH,
+                new Insets(0, 0, 10, 0), 0, 0));
+
+            //======== sortPanel ========
+            {
+                sortPanel.setBorder(new TitledBorder(null, "Sort Options", TitledBorder.LEADING, TitledBorder.TOP));
+                sortPanel.setLayout(new GridBagLayout());
+
+                //---- tempLabel ----
+                tempLabel.setToolTipText("<html>Specify a temporary working directory.  For large input files this directory will be used to <br>store intermediate results of the sort. The default is the users temp directory.");
+                tempLabel.setText("Temp Directory");
+                sortPanel.add(tempLabel, new GridBagConstraints(1, 1, 1, 1, 0.0, 1.0,
+                    GridBagConstraints.WEST, GridBagConstraints.NONE,
+                    new Insets(0, 0, 0, 0), 0, 0));
+
+                //---- recordLabel ----
+                recordLabel.setToolTipText("<html>The maximum number of records to keep in memory during the sort.  The default value is <br>500000.  Increase this number if you receive \"too many open files\" errors.   Decrease it if you <br>experience \"out of memory\" errors.");
+                recordLabel.setText("Max Records");
+                sortPanel.add(recordLabel, new GridBagConstraints(1, 2, 1, 1, 0.0, 1.0,
+                    GridBagConstraints.WEST, GridBagConstraints.NONE,
+                    new Insets(0, 0, 0, 0), 0, 0));
+
+                //---- tempButton ----
+                tempButton.setText("Browse");
+                sortPanel.add(tempButton, new GridBagConstraints(4, 1, 1, 1, 0.0, 1.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+                    new Insets(0, 0, 0, 0), 0, 0));
+                sortPanel.add(tempField, new GridBagConstraints(3, 1, 1, 1, 1.0, 1.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+                    new Insets(0, 0, 0, 0), 0, 0));
+                sortPanel.add(recordField, new GridBagConstraints(3, 2, 1, 1, 1.0, 1.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+                    new Insets(0, 0, 0, 0), 0, 0));
+            }
+            mainPanel.add(sortPanel, new GridBagConstraints(1, 3, 1, 1, 1.0, 1.0,
+                GridBagConstraints.CENTER, GridBagConstraints.BOTH,
+                new Insets(0, 0, 10, 0), 0, 0));
+
+            //======== buttonPanel ========
+            {
+                buttonPanel.setLayout(new GridBagLayout());
+
+                //---- runButton ----
+                runButton.setText("Run");
+                buttonPanel.add(runButton, new GridBagConstraints(2, 0, 1, 1, 0.0, 1.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+                    new Insets(0, 0, 0, 0), 0, 0));
+
+                //---- closeButton ----
+                closeButton.setText("Close");
+                buttonPanel.add(closeButton, new GridBagConstraints(1, 0, 1, 1, 0.0, 1.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+                    new Insets(0, 0, 0, 0), 0, 0));
+            }
+            mainPanel.add(buttonPanel, new GridBagConstraints(1, 4, 1, 1, 1.0, 0.0,
+                GridBagConstraints.CENTER, GridBagConstraints.BOTH,
+                new Insets(0, 0, 10, 0), 0, 0));
+
+            //======== OutputPanel ========
+            {
+                OutputPanel.setBorder(new TitledBorder(BorderFactory.createEmptyBorder(), "Messages", TitledBorder.LEADING, TitledBorder.TOP));
+                OutputPanel.setLayout(null);
+
+                //======== outputScroll ========
+                {
+
+                    //---- outputText ----
+                    outputText.setEditable(false);
+                    outputText.setText("");
+                    outputText.setRows(10);
+                    outputScroll.setViewportView(outputText);
+                }
+                OutputPanel.add(outputScroll);
+                outputScroll.setBounds(4, 20, 881, outputScroll.getPreferredSize().height);
+
+                { // compute preferred size
+                    Dimension preferredSize = new Dimension();
+                    for(int i = 0; i < OutputPanel.getComponentCount(); i++) {
+                        Rectangle bounds = OutputPanel.getComponent(i).getBounds();
+                        preferredSize.width = Math.max(bounds.x + bounds.width, preferredSize.width);
+                        preferredSize.height = Math.max(bounds.y + bounds.height, preferredSize.height);
+                    }
+                    Insets insets = OutputPanel.getInsets();
+                    preferredSize.width += insets.right;
+                    preferredSize.height += insets.bottom;
+                    OutputPanel.setMinimumSize(preferredSize);
+                    OutputPanel.setPreferredSize(preferredSize);
+                }
+            }
+            mainPanel.add(OutputPanel, new GridBagConstraints(1, 6, 1, 1, 1.0, 0.0,
+                GridBagConstraints.CENTER, GridBagConstraints.BOTH,
+                new Insets(0, 0, 10, 0), 0, 0));
+            mainPanel.add(separator1, new GridBagConstraints(1, 5, 1, 1, 1.0, 0.0,
+                GridBagConstraints.CENTER, GridBagConstraints.BOTH,
+                new Insets(0, 0, 10, 0), 0, 0));
+            mainPanel.add(progressBar, new GridBagConstraints(1, 7, 1, 1, 1.0, 0.0,
+                GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+                new Insets(0, 0, 0, 0), 0, 0));
+        }
+        // JFormDesigner - End of component initialization  //GEN-END:initComponents
+    }
+
+    // JFormDesigner - Variables declaration - DO NOT MODIFY  //GEN-BEGIN:variables
+    // Generated using JFormDesigner non-commercial license
+    private JPanel mainPanel;
+    private JPanel requiredPanel;
+    private JComboBox toolCombo;
+    private JLabel outputLabel;
+    private JButton outputButton;
+    private JTextField inputField;
+    private JButton inputButton;
+    private JTextField outputField;
+    private JLabel genomeLabel;
+    private JTextField genomeField;
+    private JButton genomeButton;
+    private JPanel tilePanel;
+    private JLabel zoomLabel;
+    private JLabel windowFunctionLabel;
+    private JPanel windowFunctionPanel;
+    private JCheckBox minCheckBox;
+    private JCheckBox maxCheckBox;
+    private JCheckBox meanCheckBox;
+    private JCheckBox medianCheckBox;
+    private JCheckBox a2CheckBox;
+    private JCheckBox a10CheckBox;
+    private JCheckBox a90CheckBox;
+    private JCheckBox a98CheckBox;
+    private JLabel probeLabel;
+    private JTextField probeField;
+    private JButton probeButton;
+    private JComboBox zoomCombo;
+    private JLabel windowLabel;
+    private JTextField windowField;
+    private JPanel sortPanel;
+    private JLabel tempLabel;
+    private JLabel recordLabel;
+    private JButton tempButton;
+    private JTextField tempField;
+    private JTextField recordField;
+    private JPanel buttonPanel;
+    private JButton runButton;
+    private JButton closeButton;
+    private JPanel OutputPanel;
+    private JScrollPane outputScroll;
+    private JTextArea outputText;
+    private JProgressBar progressBar;
+    // JFormDesigner - End of variables declaration  //GEN-END:variables
+}
diff --git a/src/org/broad/igv/tools/PreprocessingException.java b/src/org/broad/igv/tools/PreprocessingException.java
index 8f6bfda..35ac6dc 100644
--- a/src/org/broad/igv/tools/PreprocessingException.java
+++ b/src/org/broad/igv/tools/PreprocessingException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -26,10 +26,13 @@ package org.broad.igv.tools;
 /**
  * @author jrobinso
  */
-class PreprocessingException extends RuntimeException {
+public class PreprocessingException extends RuntimeException {
 
     public PreprocessingException(String msg) {
         super(msg);
     }
 
+    PreprocessingException(String message, Throwable cause) {
+        super(message, cause);
+    }
 }
diff --git a/src/org/broad/igv/tools/Preprocessor.java b/src/org/broad/igv/tools/Preprocessor.java
index 4dd6a4d..7597dde 100644
--- a/src/org/broad/igv/tools/Preprocessor.java
+++ b/src/org/broad/igv/tools/Preprocessor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,13 +22,12 @@
  */
 package org.broad.igv.tools;
 
+import org.broad.igv.util.collections.FloatArrayList;
+import org.broad.igv.util.collections.IntArrayList;
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
-import org.broad.igv.data.FloatArrayList;
-import org.broad.igv.data.IntArrayList;
+import org.broad.igv.Globals;
 import org.broad.igv.feature.Chromosome;
 import org.broad.igv.feature.Genome;
-import org.broad.igv.feature.GenomeManager;
 import org.broad.igv.tdf.*;
 import org.broad.igv.tools.parsers.*;
 import org.broad.igv.track.TrackType;
@@ -36,22 +35,22 @@ import org.broad.igv.track.WindowFunction;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.PrintStream;
 import java.util.*;
 
 /**
- * mean only for now
  *
  * @author jrobinso
  */
 public class Preprocessor implements DataConsumer {
 
     private static Logger log = Logger.getLogger(Preprocessor.class);
-    boolean gzipped = false;
-    int nZoom = 7;
+    boolean compressed = true;
+    private boolean skipZeroes = false;
+    private int nZoom = 7;
     int maxExtFactor = 0;
     Zoom[] zoomLevels;
     int nTracks;
-    String genomeId;
     Genome genome;
     Collection<WindowFunction> windowFunctions;
     private String currentChr = "";
@@ -63,15 +62,16 @@ public class Preprocessor implements DataConsumer {
     int lastStartPosition = 0;
     HashSet<String> skippedChromosomes = new HashSet();
     TDFWriter writer;
-    String rawType = "bed";
-    IBFDataset rawDataset;
-    IBFTile rawTile;
     Raw rawData;
     Zoom genomeZoom;
     File outputFile;
     Accumulator allDataStats;
     List<String> chromosomes = new ArrayList();
     Set<String> visitedChromosomes = new HashSet();
+    Map<String, String> attributes = new HashMap();
+    PrintStream out = System.out;
+
+
     List<WindowFunction> allDataFunctions = Arrays.asList(
             WindowFunction.mean,
             WindowFunction.median,
@@ -82,77 +82,45 @@ public class Preprocessor implements DataConsumer {
             WindowFunction.percentile90,
             WindowFunction.percentile98);
 
+
     public Preprocessor(File outputFile,
-                        String genomeId,
-                        String windowFunctions,
-                        StatusMonitor monitor,
-                        int sizeEst,
-                        boolean gzipped) {
+                        Genome genome,
+                        Collection<WindowFunction> windowFunctions,
+                        int sizeEstimate,
+                        StatusMonitor monitor) {
 
         this.statusMonitor = monitor;
         this.outputFile = outputFile;
-        this.genomeId = genomeId;
-        this.windowFunctions = parseWFS(windowFunctions);
-        this.sizeEstimate = sizeEst;
-        this.gzipped = gzipped;
-
-        // TODO -- check windowFunctions size, if zero exit with error
-
-        genome = GenomeManager.getInstance().getGenome(genomeId);
-
+        this.genome = genome;
+        this.windowFunctions = windowFunctions;
+        this.sizeEstimate = sizeEstimate;
+        this.genome = genome;
         allDataStats = new Accumulator(allDataFunctions);
-    }
-
-    public static void printUsage() {
-        System.out.println("Usage: igvtools [command] [options] inputFile outputFile genomeId");
-    }
 
-
-    /**
-     * Parse the window functions line.
-     *
-     * @param string comma delimited string of window functions, e.g. min, p10, max
-     * @return colleciton of WindowFunctions objects
-     */
-    private Collection<WindowFunction> parseWFS(String string) {
-        if (string == null || string.length() == 0) {
-            return Arrays.asList(WindowFunction.mean);
-        } else {
-            String[] tokens = string.split(",");
-            List<WindowFunction> funcs = new ArrayList(tokens.length);
-            for (int i = 0; i < tokens.length; i++) {
-                String wf = tokens[i];
-                if (wf.startsWith("p")) {
-                    wf = wf.replaceFirst("p", "percentile");
-                }
-                try {
-                    funcs.add(WindowFunction.valueOf(wf));
-                } catch (Exception e) {
-                    log.warn("Unrecognized window function: " + tokens[i]);
-                }
-            }
-            return funcs;
+        if (statusMonitor == null) {
+            statusMonitor = new CommandLineStatusMonitor();
         }
     }
 
-
     /**
      * Called to set inital parameters.  It is required that this be called
      * prior to writing the file
      */
     public void setTrackParameters(TrackType trackType, String trackLine, String[] trackNames) {
 
-        writer = new TDFWriter(outputFile, genome.getId(), trackType, trackLine, trackNames, windowFunctions, gzipped);
-        nTracks = trackNames.length;
+        if (outputFile != null && writer == null) {
+            writer = new TDFWriter(outputFile, genome.getId(), trackType, trackLine, trackNames, windowFunctions, compressed);
+            nTracks = trackNames.length;
 
-        // Convert genome coordinates from bp to kbp
-        int genomeLength = (int) (genome.getLength() / 1000);
-        genomeZoom = new Zoom(IGVConstants.CHR_ALL, 0, genomeLength);
+            // Convert genome coordinates from bp to kbp
+            int genomeLength = (int) (genome.getLength() / 1000);
+            genomeZoom = new Zoom(Globals.CHR_ALL, 0, genomeLength);
 
-        IBFGroup rootGroup = writer.getRootGroup();
-        rootGroup.setAttribute("genome", genomeId);
-        rootGroup.setAttribute("maxZoom", String.valueOf(nZoom));
+            TDFGroup rootGroup = writer.getRootGroup();
+            rootGroup.setAttribute("genome", genome.getId());
+            rootGroup.setAttribute("maxZoom", String.valueOf(nZoom));
 
+        }
     }
 
 
@@ -162,6 +130,23 @@ public class Preprocessor implements DataConsumer {
      */
     public void addData(String chr, int start, int end, float[] data, String name) {
 
+        if (writer == null) {
+            return;
+        }
+
+        if (skipZeroes) {
+            boolean allZeroes = true;
+            for (int i = 0; i < data.length; i++) {
+                if (data[i] != 0) {
+                    allZeroes = false;
+                    break;
+                }
+            }
+            if (allZeroes) {
+                return;
+            }
+        }
+
         // Check for stop signal
         if (statusMonitor != null && statusMonitor.isInterrupted()) {
             throw new PreprocessingException("Preprocessing Halted.");
@@ -176,7 +161,7 @@ public class Preprocessor implements DataConsumer {
                 String msg = "Error: Data is not sorted @ " + chr + " " + start +
                         "  (last position = " + lastStartPosition +
                         "   max ext factor = " + maxExtFactor + ")";
-                System.out.println(msg);
+                out.println(msg);
                 throw new UnsortedException(msg);
             }
         } else {
@@ -231,7 +216,7 @@ public class Preprocessor implements DataConsumer {
         if (visitedChromosomes.contains(chr)) {
             String msg = "Error: Data is not ordered by start position. Chromosome " + chr +
                     " appears in multiple blocks";
-            System.out.println(msg);
+            out.println(msg);
             throw new PreprocessingException(msg);
 
         }
@@ -240,14 +225,14 @@ public class Preprocessor implements DataConsumer {
 
         Chromosome c = genome.getChromosome(chr);
         if (c == null) {
-            System.out.println("Chromosome: " + chr + " not found in .genome file.  Skipping.");
+            out.println("Chromosome: " + chr + " not found in .genome file.  Skipping.");
             skippedChromosomes.add(chr);
         } else {
 
             chromosomes.add(chr);
 
-            System.out.println();
-            System.out.println("Processing chromosome " + chr);
+            out.println();
+            out.println("Processing chromosome " + chr);
             if (zoomLevels != null) {
                 for (Zoom zl : zoomLevels) {
                     zl.close();
@@ -259,8 +244,8 @@ public class Preprocessor implements DataConsumer {
 
             currentChr = chr;
             currentChrLength = c.getLength();
-            zoomLevels = new Zoom[nZoom + 1];
-            for (int z = 0; z <= nZoom; z++) {
+            zoomLevels = new Zoom[getNZoom() + 1];
+            for (int z = 0; z <= getNZoom(); z++) {
                 zoomLevels[z] = new Zoom(chr, z, currentChrLength);
             }
 
@@ -276,6 +261,14 @@ public class Preprocessor implements DataConsumer {
      */
     public void parsingComplete() {
 
+
+    }
+
+    public void finish() {
+        if (writer == null) {
+            return;
+        }
+
         StringBuffer chrString = new StringBuffer();
         Iterator<String> iter = chromosomes.iterator();
         while (iter.hasNext()) {
@@ -286,6 +279,10 @@ public class Preprocessor implements DataConsumer {
         }
         writer.getRootGroup().setAttribute("chromosomes", chrString.toString());
 
+        for (Map.Entry<String, String> entry : attributes.entrySet()) {
+            writer.getRootGroup().setAttribute(entry.getKey(), entry.getValue());
+        }
+
         if (zoomLevels != null) {
             for (Zoom zl : zoomLevels) {
                 zl.close();
@@ -293,21 +290,27 @@ public class Preprocessor implements DataConsumer {
         }
         genomeZoom.close();
 
-        rawData.close();
+        if (rawData == null) {
+            // TODO -- delete .tdf file?
+            out.println("No features were found that matched chromosomes in genome: " + genome.getId());
 
-        // Record max/min
-        allDataStats.finish();
-        IBFGroup group = writer.getGroup("/");
-
-        for (WindowFunction wf : allDataFunctions) {
-            group.setAttribute(wf.getDisplayName(), String.valueOf(allDataStats.getValue(wf)));
+        } else {
+            rawData.close();
+
+            // Record max/min
+            allDataStats.finish();
+            TDFGroup group = writer.getGroup("/");
+            group.setAttribute(TDFGroup.USE_PERCENTILE_AUTOSCALING, "true");
+            for (WindowFunction wf : allDataFunctions) {
+                group.setAttribute(wf.getDisplayName(), String.valueOf(allDataStats.getValue(wf)));
+            }
+            writer.closeFile();
         }
-        writer.closeFile();
 
         if (statusMonitor != null) {
             statusMonitor.setPercentComplete(100);
         } else {
-            System.out.println("Done");
+            out.println("Done");
         }
     }
 
@@ -316,14 +319,14 @@ public class Preprocessor implements DataConsumer {
         //this.type = type;
     }
 
-    public void setTrackLine(String trackLine) {
-    }
-
-
     public void setSortTolerance(int tolerance) {
         maxExtFactor = tolerance;
     }
 
+    public void setAttribute(String key, String value) {
+        attributes.put(key, value);
+    }
+
     /**
      * @param sizeEstimate the sizeEstimate to set
      */
@@ -331,6 +334,22 @@ public class Preprocessor implements DataConsumer {
         this.sizeEstimate = sizeEstimate;
     }
 
+    public void setSkipZeroes(boolean skipZeroes) {
+        this.skipZeroes = skipZeroes;
+    }
+
+    public int getNZoom() {
+        return nZoom;
+    }
+
+    public void setNZoom(int nZoom) {
+        this.nZoom = nZoom;
+    }
+
+    public int getSizeEstimate() {
+        return sizeEstimate;
+    }
+
 
     /**
      * Class representing a tile of raw (as opposed to summarized) data.
@@ -350,18 +369,14 @@ public class Preprocessor implements DataConsumer {
             this.tileNumber = tileNumber;
             this.tileStart = start;
             this.tileEnd = end;
-            startArray = new IntArrayList(10000);
-            endArray = new IntArrayList(10000);
+            startArray = new IntArrayList();
+            endArray = new IntArrayList();
             dataArray = new FloatArrayList[nTracks];
             for (int i = 0; i < nTracks; i++) {
-                dataArray[i] = new FloatArrayList(10000);
+                dataArray[i] = new FloatArrayList();
             }
         }
 
-        void addData(int start, int end, float[] data) {
-            addData(start, end, data, null);
-        }
-
         void addData(int start, int end, float[] data, String name) {
 
 
@@ -376,7 +391,7 @@ public class Preprocessor implements DataConsumer {
 
 
             if (name != null && nameList == null) {
-                nameList = new ArrayList(10000);
+                nameList = new ArrayList();
             }
 
 
@@ -395,9 +410,10 @@ public class Preprocessor implements DataConsumer {
         void close() {
             try {
                 if (startArray.size() > 0) {
+
                     int[] s = startArray.toArray();
                     int[] e = endArray.toArray();
-                    float[][] d = new float[dataArray.length][dataArray[0].getSize()];
+                    float[][] d = new float[dataArray.length][dataArray[0].size()];
                     for (int i = 0; i < dataArray.length; i++) {
                         d[i] = dataArray[i].toArray();
                     }
@@ -427,7 +443,7 @@ public class Preprocessor implements DataConsumer {
 
         String chr;
         String dsName;
-        IBFDataset dataset;
+        TDFDataset dataset;
         int tileWidth;
         Map<Integer, RawTile> activeTiles = new HashMap();
 
@@ -436,7 +452,7 @@ public class Preprocessor implements DataConsumer {
             this.tileWidth = tileWidth;
             int nTiles = (int) (chrLength / tileWidth) + 1;
             dsName = "/" + chr + "/raw";
-            dataset = writer.createDataset(dsName, IBFDataset.DataType.FLOAT, tileWidth, nTiles);
+            dataset = writer.createDataset(dsName, TDFDataset.DataType.FLOAT, tileWidth, nTiles);
 
         }
 
@@ -458,7 +474,7 @@ public class Preprocessor implements DataConsumer {
                     RawTile t = activeTiles.get(tileNumber);
                     t.close();
                     activeTiles.remove(tileNumber);
-                    //System.out.println("(-) " + level + "_" + tileNumber);
+                    //out.println("(-) " + level + "_" + tileNumber);
                 } else {
                     break;
                 }
@@ -476,7 +492,7 @@ public class Preprocessor implements DataConsumer {
 
             // Update progress -- assume uniform distribution
             if (statusMonitor != null) {
-                int p = (int) ((100.0 * nPtsProcessed) / (1.5 * sizeEstimate));
+                int p = (int) ((100.0 * nPtsProcessed) / (1.5 * getSizeEstimate()));
                 if (p > percentComplete) {
                     percentComplete = p;
                     statusMonitor.setPercentComplete(percentComplete);
@@ -502,13 +518,9 @@ public class Preprocessor implements DataConsumer {
 
         int level;
         int tileWidth;
-        int nBins = 700;
         LinkedHashMap<Integer, Tile> activeTiles = new LinkedHashMap();
-        Map<WindowFunction, IBFDataset> datasets = new HashMap();
-        double sparsityMetric = 0;
-        int maximumTileCount = 0;
+        Map<WindowFunction, TDFDataset> datasets = new HashMap();
 
-        //IBFDataset dataset;
 
         Zoom(String chr, int level, int chrLength) {
             int nTiles = (int) Math.pow(2, level);
@@ -518,7 +530,7 @@ public class Preprocessor implements DataConsumer {
             // Create datasets -- one for each window function
             for (WindowFunction wf : windowFunctions) {
                 String dsName = "/" + chr + "/z" + level + "/" + wf.toString();
-                datasets.put(wf, writer.createDataset(dsName, IBFDataset.DataType.FLOAT, tileWidth, nTiles));
+                datasets.put(wf, writer.createDataset(dsName, TDFDataset.DataType.FLOAT, tileWidth, nTiles));
             }
         }
 
@@ -528,7 +540,6 @@ public class Preprocessor implements DataConsumer {
             int endTile = end / tileWidth;
 
             // Check for closed tiles
-
             int tmp = (start - maxExtFactor) / tileWidth;
             while (!activeTiles.isEmpty()) {
                 Integer tileNumber = activeTiles.keySet().iterator().next();
@@ -536,10 +547,6 @@ public class Preprocessor implements DataConsumer {
                     Tile t = activeTiles.get(tileNumber);
                     t.close();
                     activeTiles.remove(tileNumber);
-
-                    sparsityMetric = Math.max(sparsityMetric, t.sparsityMetric());
-                    maximumTileCount = Math.max(maximumTileCount, t.totalCount);
-                    //System.out.println("(-) " + level + "_" + tileNumber);
                 } else {
                     break;
                 }
@@ -547,9 +554,8 @@ public class Preprocessor implements DataConsumer {
 
 
             for (int i = startTile; i <= endTile; i++) {
-                Tile t = activeTiles.get(startTile);
+                Tile t = activeTiles.get(i);
                 if (t == null) {
-                    //System.out.println("(+) " + level + "_" + i);
                     t = new Tile(datasets, level, i, 700, tileWidth);
                     activeTiles.put(i, t);
                 }
@@ -558,6 +564,7 @@ public class Preprocessor implements DataConsumer {
         }
 
         // Close all active tiles
+
         public void close() {
             for (Tile t : activeTiles.values()) {
                 t.close();
@@ -569,8 +576,6 @@ public class Preprocessor implements DataConsumer {
      * A tile of summarized data
      */
     class Tile {
-
-        public static final int DATA_COUNT_THRESHOLD = 5000;
         int totalCount;
         int zoomLevel;
         int tileNumber;
@@ -580,9 +585,9 @@ public class Preprocessor implements DataConsumer {
         int nBins;
         int nonEmptyBins;
         Accumulator[][] accumulators;
-        Map<WindowFunction, IBFDataset> datasets;
+        Map<WindowFunction, TDFDataset> datasets;
 
-        Tile(Map<WindowFunction, IBFDataset> datasets, int zoomLevel, int tileNumber, int nBins, int tileWidth) {
+        Tile(Map<WindowFunction, TDFDataset> datasets, int zoomLevel, int tileNumber, int nBins, int tileWidth) {
             this.totalCount = 0;
             this.datasets = datasets;
             this.zoomLevel = zoomLevel;
@@ -610,9 +615,6 @@ public class Preprocessor implements DataConsumer {
 
 
             for (int t = 0; t < nTracks; t++) {
-
-                // Close bins that will not be visited again.  This is neccessary
-                // to free resources held by quantile accumulators
                 for (int b = lastFinishedBin; b < tmp; b++) {
                     if (accumulators[t][b] != null) {
                         accumulators[t][b].finish();
@@ -630,10 +632,6 @@ public class Preprocessor implements DataConsumer {
         }
 
 
-        double sparsityMetric() {
-            return ((double) nonEmptyBins) / nBins;
-        }
-
         /**
          *
          */
@@ -642,7 +640,6 @@ public class Preprocessor implements DataConsumer {
             // Count non-empty bins.  All tracks should be the same
             nonEmptyBins = 0;
 
-            //System.out.println("Closing tile: " + zoomLevel + "-" + tileNumber);
             for (int t = 0; t < nTracks; t++) {
                 for (int i = 0; i < nBins; i++) {
                     if (accumulators[t][i] != null) {
@@ -654,7 +651,7 @@ public class Preprocessor implements DataConsumer {
                 }
             }
 
-            IBFTile tile = null;
+            TDFTile tile = null;
 
             for (WindowFunction wf : datasets.keySet()) {
 
@@ -676,7 +673,7 @@ public class Preprocessor implements DataConsumer {
                             }
                         }
                     }
-                    tile = new IBFVaryTile((int) tileStart, binWidth, starts, data);
+                    tile = new TDFVaryTile((int) tileStart, binWidth, starts, data);
 
                 } else {
                     float[][] data = new float[nTracks][nBins];
@@ -685,7 +682,7 @@ public class Preprocessor implements DataConsumer {
                             data[t][i] = accumulators[t][i] == null ? Float.NaN : accumulators[t][i].getValue(wf);
                         }
                     }
-                    tile = new IBFFixedTile((int) tileStart, (int) tileStart, binWidth, data);
+                    tile = new TDFFixedTile(tileStart, tileStart, binWidth, data);
                 }
 
                 String dsName = datasets.get(wf).getName();
@@ -706,45 +703,47 @@ public class Preprocessor implements DataConsumer {
                 ext.equalsIgnoreCase(".bedz") || ext.equalsIgnoreCase(".bed");
     }
 
-    public void preprocess(String iFile, String oFile, String probeFile, String command, int maxZoomValue, int windowSizeValue,
-                           int extFactorValue) throws IOException {
-
-        try {
-
-            nZoom = maxZoomValue;
-            String extension = getExtension(iFile);
-            if (command.equals("count") || (command.equals("pre") && isAlignmentFile(extension))) {
-                setTrackParameters(TrackType.OTHER, null, new String[]{iFile});
-                CoverageCounter aParser = new CoverageCounter(iFile, this, windowSizeValue, extFactorValue);
-                setSizeEstimate((int) (genome.getLength() / windowSizeValue));
-
-                aParser.parse();
-            } else {
-                String tmp = iFile.toLowerCase();
-                if (tmp.endsWith(".txt")) tmp = tmp.substring(0, tmp.length() - 4);
-
-                if (tmp.endsWith("wig") || tmp.endsWith("wig.gz")) {
-                    WiggleParser wg = new WiggleParser(iFile, this, genome);
-                    wg.parse();
-                } else if (tmp.endsWith(".cn") || tmp.endsWith(".cn.gz") ||
-                        tmp.endsWith(".igv") || tmp.endsWith(".snp")) {
-                    CNParser cnParser = new CNParser(iFile, this, genome);
-                    cnParser.parse();
-                } else if (tmp.endsWith("gct") || tmp.endsWith("gct.gz")) {
-                    GCTParser gctParser = new GCTParser(this, new File(iFile), probeFile, genome.getId());
-                    gctParser.parse();
-                } else {
-                    System.out.println("Error: cannot 'tile' files of type " + extension);
-                    System.out.println("Valid file extensions are: .cn, .xcn, .igv, .snp, .wig, and .gct");
-                }
-            }
-        } catch (RuntimeException e) {
-            // Any exception caught here should already be logged
-            (new File(oFile)).delete();
-            throw e;
+    public void count(String iFile, int windowSizeValue, int extFactorValue, int maxZoomValue,
+                      File wigFile, int strandOption) throws IOException {
+        setNZoom(maxZoomValue);
+        setTrackParameters(TrackType.COVERAGE, null, new String[]{iFile});
+        this.setSkipZeroes(true);
+        CoverageCounter aParser = new CoverageCounter(iFile, this, windowSizeValue, extFactorValue, wigFile, genome, strandOption);
+        setSizeEstimate((int) (genome.getLength() / windowSizeValue));
+        aParser.parse();
+    }
+
+    public void preprocess(File iFile, String probeFile, int maxZoomValue) throws IOException {
+
+        setNZoom(maxZoomValue);
+
+        String tmp = iFile.getAbsolutePath().toLowerCase();
+        if (tmp.endsWith(".txt")) tmp = tmp.substring(0, tmp.length() - 4);
+
+        if (tmp.endsWith("wig") || tmp.endsWith("wig.gz") ||
+                tmp.endsWith("bedgraph") || tmp.endsWith("bedgraph.gz")) {
+            WiggleParser wg = new WiggleParser(iFile.getAbsolutePath(), this, genome);
+            wg.parse();
+        } else if (tmp.endsWith(".cn") || tmp.endsWith(".igv") || tmp.endsWith(".cn.gz") ||
+                tmp.endsWith(".cn") || tmp.endsWith(".snp")) {
+            CNParser cnParser = new CNParser(iFile.getAbsolutePath(), this, genome);
+            cnParser.parse();
+        } else if (tmp.endsWith("gct") || tmp.endsWith("gct.gz")) {
+            GCTParser gctParser = new GCTParser(this, iFile, probeFile, genome.getId());
+            gctParser.parse();
+        } else {
+            String extension = getExtension(iFile.getAbsolutePath());
+            out.println("Error: cannot 'tile' files of type " + extension);
+            out.println("Valid file extensions are: .cn, .xcn, .cn, .snp, .wig, and .gct");
         }
     }
 
+    /**
+     * Strip of "gz" and "txt" extensions to get to true format extension.
+     *
+     * @param filename
+     * @return
+     */
     public static String getExtension(String filename) {
 
         if (filename.endsWith(".gz")) {
diff --git a/src/org/broad/igv/tools/StatusMonitor.java b/src/org/broad/igv/tools/StatusMonitor.java
index 54015ce..2e53e84 100644
--- a/src/org/broad/igv/tools/StatusMonitor.java
+++ b/src/org/broad/igv/tools/StatusMonitor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/tools/TestFileGenerator.java b/src/org/broad/igv/tools/TestFileGenerator.java
index c8e001c..67afe61 100644
--- a/src/org/broad/igv/tools/TestFileGenerator.java
+++ b/src/org/broad/igv/tools/TestFileGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,6 +23,7 @@
 package org.broad.igv.tools;
 
 import net.sf.samtools.util.AsciiLineReader;
+import org.broad.igv.util.IGVHttpUtils;
 
 import java.io.*;
 import java.net.URL;
@@ -39,12 +40,12 @@ public class TestFileGenerator {
 
     public static void main(String[] args) {
 
-        generateTestFile("/Users/jrobinso/IGV/test.cn", true, 10000, 20);
+        generateTestFile("/Users/jrobinso/IGV/test_25thousand.gct", true, 25000, 100);
     }
 
     enum FileType {
 
-        CN, IGV, GCT
+        CN, IGV, GCT, WIG
     }
 
     ;
@@ -67,6 +68,9 @@ public class TestFileGenerator {
         } else if (outputFile.endsWith(".gct")) {
             type = FileType.GCT;
 
+        } else if (outputFile.endsWith(".wig")) {
+            type = FileType.WIG;
+
         } else {
             System.out.println("Unsupported file type: " + outputFile);
         }
@@ -74,7 +78,6 @@ public class TestFileGenerator {
         try {
             pw = new PrintWriter(new FileWriter(outputFile));
             writeHeader(pw, nSamples, type);
-            pw.println();
 
             Random rand = new Random();
             List<String> probes = type == FileType.GCT ? getExpressionProbes() : new ArrayList();
@@ -87,44 +90,58 @@ public class TestFileGenerator {
                 double center = -3;
                 int lastStart = 0;
 
+                if (type == FileType.WIG) {
+                    int step = (int) delta;
+                    pw.println("fixedStep chrom=" + chr + " start=" + 1 + " step=" + step + " span=" + (step - 1));
+                }
+
                 for (int r = 0; r <= rowsPerChr; r++) {
 
                     int start = getStart(len, lastStart, r, delta, rand, sorted);
                     lastStart = start + 1;
 
-                    center = -1.5 + 3 * ((double) start / len);
-
-                    if (type == FileType.CN) {
-                        pw.print("Snp_" + r + "\t" + chr + "\t" + start);
-
-                    } else if (type == FileType.IGV) {
-                        int end = Math.max(start + 1, (int) (start + Math.random() * 1000));
-                        pw.print(chr + "\t" + start + "\t" + end + "\tProbe_" + r);
-
-                    } else if (type == FileType.GCT) {
-                        String probe = probes.get(n);
-                        pw.print(probe + "\tdesc_" + n);
-                    }
-
-                    for (int s = 0; s < nSamples; s++) {
-                        double v = center + rand.nextGaussian();
-                        pw.print("\t" + v);
-                    }
-
-                    pw.println();
-                    n++;
-                    if (n >= probes.size()) {
-                        n = 0;
+                    center = 3 * ((double) start / len);
+
+                    if (type == FileType.WIG) {
+                        pw.println(center + rand.nextGaussian());
+
+                    } else {
+                        switch (type) {
+                            case CN:
+                                pw.print("Snp_" + r + "\t" + chr + "\t" + start);
+                                break;
+                            case IGV:
+                                int end = Math.max(start + 1, (int) (start + Math.random() * 1000));
+                                pw.print(chr + "\t" + start + "\t" + end + "\tProbe_" + r);
+                                break;
+                            case GCT:
+                                String probe = probes.get(n);
+                                pw.print(probe + "\tdesc_" + n);
+                        }
+
+                        for (int s = 0; s < nSamples; s++) {
+                            double v = center + rand.nextGaussian();
+                            pw.print("\t" + v);
+                        }
+
+                        pw.println();
+                        n++;
+                        if (n >= probes.size()) {
+                            n = 0;
+                        }
                     }
                 }
             }
-
-
         } catch (IOException e) {
             System.out.println("Error: " + e.getMessage());
-        } finally {
+        }
+
+        finally
+
+        {
             pw.close();
         }
+
     }
 
     public static int getChromosomeIndex(int i, boolean sorted) {
@@ -151,11 +168,13 @@ public class TestFileGenerator {
             for (int s = 0; s < nSamples; s++) {
                 pw.print("\tSample_" + s);
             }
+            pw.println();
         } else if (type == FileType.IGV) {
             pw.print("Chr\tStart\tEnd\tProbe");
             for (int s = 0; s < nSamples; s++) {
                 pw.print("\tSample_" + s);
             }
+            pw.println();
         } else if (type == FileType.GCT) {
             pw.println("ignored");
             pw.println("ignored");
@@ -163,6 +182,7 @@ public class TestFileGenerator {
             for (int s = 0; s < nSamples; s++) {
                 pw.print("\tSample_" + s);
             }
+            pw.println();            
         }
     }
 
@@ -173,9 +193,10 @@ public class TestFileGenerator {
         try {
             InputStream is = null;
 
-            if (urlString.startsWith("http") || urlString.startsWith("file")) {
+            if (urlString.startsWith("http:") || urlString.startsWith("https:") ||
+                    urlString.startsWith("ftp:") || urlString.startsWith("file")) {
                 URL url = new URL(urlString);
-                URLConnection connection = url.openConnection();
+                URLConnection connection = IGVHttpUtils.openConnection(url);
                 is = connection.getInputStream();
             } else {
                 is = new FileInputStream(urlString);
diff --git a/src/org/broad/igv/tools/UCSCUtils.java b/src/org/broad/igv/tools/UCSCUtils.java
index 745ce50..dd08d99 100644
--- a/src/org/broad/igv/tools/UCSCUtils.java
+++ b/src/org/broad/igv/tools/UCSCUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -29,6 +29,26 @@ import java.io.*;
  */
 public class UCSCUtils {
 
+
+    public static void main(String[] args) {
+
+        if (args.length < 3) {
+            System.out.println("Usage convertWIGVile <txtFile> <wibFile> <wigFile> [trackLine options]");
+            System.exit(-1);
+        }
+        File txtFile = new File(args[0]);
+        File wibFile = new File(args[1]);
+        File wigFile = new File(args[2]);
+        String trackLine = null;
+        if(args.length > 3) {
+            trackLine = "track";
+            for(int i=3; i<args.length; i++) {
+                trackLine += (" " + args[i]);
+            }
+        }
+        convertWIBFile(txtFile, wibFile, wigFile, trackLine);
+    }
+
     /**
      * 0 bin smallint(5) unsigned not null,	# bin scheme for indexing
      * 1  chrom varchar(255) not null,        # Human chromosome or FPC contig
@@ -132,16 +152,4 @@ public class UCSCUtils {
         }
     }
 
-    public static void main(String[] args) {
-
-        if (args.length < 3) {
-            System.out.println("Usage convertWIGVile <txtFile> <wibFile> <wigFile> [trackLine]");
-            System.exit(-1);
-        }
-        File txtFile = new File(args[0]);
-        File wibFile = new File(args[1]);
-        File wigFile = new File(args[2]);
-        String trackLine = args.length > 3 ? args[3] : null;
-        convertWIBFile(txtFile, wibFile, wigFile, trackLine);
-    }
 }
diff --git a/src/org/broad/igv/tools/converters/IlluminaToCN.java b/src/org/broad/igv/tools/converters/IlluminaToCN.java
new file mode 100644
index 0000000..7ae1e79
--- /dev/null
+++ b/src/org/broad/igv/tools/converters/IlluminaToCN.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.tools.converters;
+
+import org.broad.igv.data.seg.Segment;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * User: jrobinso
+ * Date: Apr 19, 2010
+ */
+public class IlluminaToCN {
+    static int probeIdCol = 1;
+    static int chrCol = 3;
+    static int posCol = 4;
+    static int preColCount = 10;
+    static int sampleColumnCount = 16;
+    static int cnvOffset = 5;
+    static int logROffset = 7;
+    static int afOffset = 8;
+
+    /**
+     * Converts an illumina aCGH file to multiple .seg and .cn files.  The files are produced
+     *
+     * @param iFile
+     * @param oFilePrefix
+     */
+
+    public static void convertFile(String iFile, String oFilePrefix) {
+
+        BufferedReader reader = null;
+        PrintWriter cnvWriter = null;
+        PrintWriter logRWriter = null;
+        PrintWriter afWriter = null;
+
+        LinkedHashMap<String, List<Segment>> cnvSegments = new LinkedHashMap();
+
+        try {
+            reader = new BufferedReader(new FileReader(iFile));
+            cnvWriter = new PrintWriter(new BufferedWriter(new FileWriter(oFilePrefix + ".cnv.cn")));
+            logRWriter = new PrintWriter(new BufferedWriter(new FileWriter(oFilePrefix + ".logr.cn")));
+            afWriter = new PrintWriter(new BufferedWriter(new FileWriter(oFilePrefix + ".af.cn")));
+            List<PrintWriter> allWriters = Arrays.asList(logRWriter, afWriter);
+
+            String nextLine = reader.readLine();
+            List<String> samples = getSamples(nextLine);
+
+            for (PrintWriter pw : allWriters) {
+                pw.print("Probe\tChr\tPosition");
+                for (String s : samples) {
+                    pw.print("\t" + s);
+                }
+                pw.println();
+            }
+            cnvWriter.println("Sample\tchr\tstart\tend\tnumberOfSnps");
+
+
+            Map<String, Segment> currentSegments = new HashMap();
+
+            while ((nextLine = reader.readLine()) != null) {
+                String[] tokens = nextLine.split("\t");
+
+                String probe = tokens[probeIdCol];
+                String chr = "chr" + tokens[chrCol];
+                int position = Integer.parseInt(tokens[posCol]);
+
+
+                for (PrintWriter pw : allWriters) {
+                    pw.print(probe + "\t" + chr + "\t" + position);
+                }
+
+                int col = preColCount - 1;
+                for (int i = 0; i < samples.size(); i++) {
+                    String sample = samples.get(i);
+
+                    int cnv = (int) Float.parseFloat(tokens[col + cnvOffset]);
+
+                    Segment segment = currentSegments.get(sample);
+                    if (segment == null || !chr.equals(segment.getChr()) || cnv == (int) segment.getScore()) {
+                        if (segment != null) {
+                            List<Segment> segs = cnvSegments.get(sample);
+                            if (segs == null) {
+                                segs = new ArrayList();
+                                cnvSegments.put(sample, segs);
+                            }
+                            segs.add(segment);
+                        }
+                        segment = new Segment(position, position, cnv, 1);
+                        currentSegments.put(sample, segment);
+                    } else {
+
+                        segment.incremenetSnpCount(1);
+                        segment.setEnd(position);
+                    }
+
+
+                    String logR = tokens[col + logROffset];
+                    logRWriter.print("\t" + logR);
+
+                    String af = tokens[col + afOffset];
+                    afWriter.print("\t" + af);
+
+                    col += sampleColumnCount;
+                }
+                for (PrintWriter pw : allWriters) {
+                    pw.println();
+                }
+            }
+
+            for (Map.Entry<String, List<Segment>> entry : cnvSegments.entrySet()) {
+                String sample = entry.getKey();
+                for (Segment segment : entry.getValue()) {
+                    cnvWriter.println(sample + "\t" + segment.getChr() + "\t" + segment.getStart() + "\t"
+                            + segment.getEnd() + "\t" + segment.getSnpCount() + "\t" + (int) segment.getScore());
+                }
+            }
+
+        }
+        catch (IOException e) {
+            e.printStackTrace();
+        }
+        finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException e) {
+                }
+            }
+            for (PrintWriter pw : Arrays.asList(cnvWriter, logRWriter, afWriter)) {
+                if (pw != null) {
+                    pw.close();
+                }
+            }
+
+        }
+
+    }
+
+
+    //SB516834_PB15681_E02.GType
+
+    private static List<String> getSamples(String nextLine) {
+        String[] tokens = nextLine.split("\t");
+        int col = preColCount;
+
+        List<String> samples = new ArrayList();
+        while (col < tokens.length - 1) {
+            samples.add(tokens[col].replace(".GType", ""));
+            col += sampleColumnCount;
+        }
+        return samples;
+    }
+
+    public static void main(String[] args) {
+        String ifile = "/Users/jrobinso/IGV/maggie/FullDataTable2.txt";
+        String oFilePre = "/Users/jrobinso/IGV/maggie/smallDataTable";
+        convertFile(ifile, oFilePre);
+    }
+}
diff --git a/src/org/broad/igv/tools/parsers/AbstractParser.java b/src/org/broad/igv/tools/parsers/AbstractParser.java
index bc64c13..a4ae3f7 100644
--- a/src/org/broad/igv/tools/parsers/AbstractParser.java
+++ b/src/org/broad/igv/tools/parsers/AbstractParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -25,8 +25,6 @@ package org.broad.igv.tools.parsers;
 
 import org.broad.igv.track.TrackType;
 
-import java.util.HashMap;
-import java.util.Map;
 import java.util.Hashtable;
 
 /**
diff --git a/src/org/broad/igv/tools/parsers/CNParser.java b/src/org/broad/igv/tools/parsers/CNParser.java
index 0b4ad81..0a7a404 100644
--- a/src/org/broad/igv/tools/parsers/CNParser.java
+++ b/src/org/broad/igv/tools/parsers/CNParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,11 +22,11 @@
  */
 package org.broad.igv.tools.parsers;
 
-import org.broad.igv.util.ParsingUtils;
+import org.broad.igv.feature.Genome;
 import org.broad.igv.track.TrackType;
 import org.broad.igv.util.AsciiLineReader;
+import org.broad.igv.util.ParsingUtils;
 import org.broad.igv.util.ResourceLocator;
-import org.broad.igv.feature.Genome;
 
 import java.io.IOException;
 import java.util.Set;
@@ -171,7 +171,7 @@ public class CNParser extends AbstractParser {
 
                 try {
 
-                    chr = genome.getChromosomeAlias (tokens[chrColumn]);
+                    chr = (genome == null ? tokens[chrColumn] : genome.getChromosomeAlias(tokens[chrColumn]));
                     if (!chr.equals(lastChr)) {
                         newChromosome();
                     }
diff --git a/src/org/broad/igv/tools/parsers/DataConsumer.java b/src/org/broad/igv/tools/parsers/DataConsumer.java
index 5ec0acc..374dda3 100644
--- a/src/org/broad/igv/tools/parsers/DataConsumer.java
+++ b/src/org/broad/igv/tools/parsers/DataConsumer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -33,8 +33,6 @@ public interface DataConsumer {
 
     public void addData(String chr, int start, int end, float[] data, String name);
 
-    //public void newChromosome(String chr);
-
     public void parsingComplete();
 
     public void setTrackParameters(TrackType trackType, String trackLine, String[] trackNames);
@@ -47,4 +45,7 @@ public interface DataConsumer {
      */
     public void setSortTolerance(int tolerance);
 
+    public void setAttribute(String key, String value);
+
+
 }
diff --git a/src/org/broad/igv/tools/parsers/GCTParser.java b/src/org/broad/igv/tools/parsers/GCTParser.java
index 27716a3..9d914a3 100644
--- a/src/org/broad/igv/tools/parsers/GCTParser.java
+++ b/src/org/broad/igv/tools/parsers/GCTParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,8 +22,7 @@
  */
 package org.broad.igv.tools.parsers;
 
-import cern.colt.list.IntArrayList;
-import cern.colt.list.ObjectArrayList;
+import org.broad.igv.util.collections.IntArrayList;
 import org.apache.log4j.Logger;
 import org.broad.igv.data.GCTDataConsumer;
 import org.broad.igv.data.ProbeSet;
@@ -56,21 +55,13 @@ public class GCTParser extends AbstractParser {
 
 
     ResourceLocator dataFileLocator;
-        Map<String, Feature> probeLocusMap;
+    Map<String, Feature> probeLocusMap;
     FileType type;
     int dataStartColumn;
     int probeColumn;
     int descriptionColumn;
     String genome;
-    ProbeSet probeSet;
-    /**
-     * Flag to record server connect failures
-     */
-    boolean canConnectToServer = true;
-    /**
-     * Flag to record connect load user probe mapping file
-     */
-    boolean canLoadProbeMapping = true;
+
     /**
      * Map colum heading -> index for effecient reverse lookup
      */
@@ -79,13 +70,7 @@ public class GCTParser extends AbstractParser {
     StatusMonitor statusMonitor;
     GeneManager geneManager = null;
 
-    /**
-     * Constructs ...
-     *
-     * @param resFile
-     * @param probeFile
-     * @param genome
-     */
+
     public GCTParser(DataConsumer dataConsumer, File resFile, String probeFile, String genome) throws IOException {
         this(dataConsumer, new ResourceLocator(resFile.getAbsolutePath()), probeFile, genome);
     }
@@ -94,13 +79,8 @@ public class GCTParser extends AbstractParser {
         super(dataConsumer);
         this.dataFileLocator = resFile;
         this.genome = genome;
-        // if (IGVModel.getInstance().getViewContext().getGenomeId() != null &&
-        //         IGVModel.getInstance().getViewContext().getGenomeId().equals(genome)) {
-        //     this.geneManager = TrackManager.getInstance().getGeneManager();
-        //} else {
         this.geneManager = GeneManager.getGeneManager(genome);
-        //}
-        if (probeFile != null) {
+        if (probeFile != null && probeFile.trim().length() > 0) {
             BEDFileParser parser = new BEDFileParser();
             ResourceLocator rl = new ResourceLocator(probeFile);
             AsciiLineReader reader = ParsingUtils.openAsciiReader(rl);
@@ -114,42 +94,6 @@ public class GCTParser extends AbstractParser {
 
     }
 
-    /*
-     */
-    public static boolean parsableMAGE_TAB(ResourceLocator file) {
-        AsciiLineReader reader = null;
-        try {
-            reader = ParsingUtils.openAsciiReader(file);
-            String nextLine = null;
-
-            //skip first row
-            reader.readLine();
-
-            //check second row for MAGE_TAB identifiers
-            if ((nextLine = reader.readLine()) != null && (nextLine.contains("Reporter REF") || nextLine.contains("Composite Element REF") || nextLine.contains("Term Source REF") || nextLine.contains("CompositeElement REF") || nextLine.contains("TermSource REF") || nextLine.contains("Coordinates REF"))) {
-                int count = 0;
-                // check if this mage_tab data matrix can be parsed by this class
-                while ((nextLine = reader.readLine()) != null && count < 5) {
-                    nextLine = nextLine.trim();
-                    if (nextLine.startsWith("SNP_A") || nextLine.startsWith("CN_")) {
-                        return false;
-                    }
-
-                    count++;
-                }
-                return true;
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-            return false;
-        } finally {
-            if (reader != null) {
-                reader.close();
-            }
-        }
-
-        return false;
-    }
 
 
     /**
@@ -251,11 +195,11 @@ public class GCTParser extends AbstractParser {
             int nTokens = ParsingUtils.split(headerLine, tokens, '\t');
 
             int nColumns = (nTokens - dataStartColumn) / skip;
-            ObjectArrayList columnHeadingsObj = new ObjectArrayList();
+            ArrayList columnHeadingsObj = new ArrayList();
             for (int i = 0; i < nColumns; i++) {
                 String heading = tokens[dataStartColumn + i * skip].replace('\"', ' ').trim();
                 if (type == FileType.MAGE_TAB) {
-                    if (!columnHeadingsObj.contains(heading, true)) {
+                    if (!columnHeadingsObj.contains(heading)) {
                         columnHeadingsObj.add(heading);
                         headingIndexMap.put(heading, columnHeadingsObj.size() - 1);
                     }
@@ -304,6 +248,7 @@ public class GCTParser extends AbstractParser {
 
             int lineCount = 0;
 
+            log.debug("Begin feature parsing");
 
             while ((nextLine = reader.readLine()) != null) {
                 nTokens = ParsingUtils.split(nextLine, tokens, '\t');
@@ -392,10 +337,7 @@ public class GCTParser extends AbstractParser {
             int[] ends = getEndLocations(chr);
             String[] name = getNames(chr);
             for (int i = 0; i < starts.length; i++) {
-                float[] data = new float[columnHeadings.length];
-                for (int c = 0; c < columnHeadings.length; c++) {
-                    data[c] = getData(columnHeadings[c], chr)[i];
-                }
+                float[] data = getData(chr, i);
                 getDataConsumer().addData(chr, starts[i], ends[i], data, name[i]);
             }
         }
@@ -413,9 +355,6 @@ public class GCTParser extends AbstractParser {
      */
     public void addRow(String probeId, String description, float[] values, char[] calls) {
 
-        if (log.isDebugEnabled()) {
-            log.debug("Adding row for probe: " + probeId);
-        }
 
         // TODO -- simplify this with the use of a locus interface
         String chr = null;
@@ -428,14 +367,6 @@ public class GCTParser extends AbstractParser {
             String[] locusStrings = getExplicitLocusStrings(description);
             if (locusStrings != null) {
 
-                if (log.isDebugEnabled()) {
-                    String tmp = "";
-                    for (String ls : locusStrings) {
-                        tmp += ls;
-                    }
-                    log.debug("Found locus string in description: " + probeId + " -> " + tmp);
-
-                }
 
                 for (String ls : locusStrings) {
                     ls = ls.trim();
@@ -455,20 +386,11 @@ public class GCTParser extends AbstractParser {
             Locus locus = getLocus(probeId);
             if ((locus != null) && locus.isValid()) {
 
-                if (log.isDebugEnabled()) {
-                    log.debug("Probe is a locus: " + probeId);
-                }
-
                 addRow(probeId, locus, values);
                 return;
             }
         }
 
-
-        // and to loop through the list
-        if (log.isDebugEnabled()) {
-            log.debug("Searching probeToGene map");
-        }
         String[] loci = ProbeToGeneMap.getInstance().getGenesForProbe(probeId);
 
         if (loci != null) {
@@ -476,11 +398,6 @@ public class GCTParser extends AbstractParser {
                 Locus locus = getLocus(locusString);
                 //Feature gene = FeatureDB.getFeature(locusString);
 
-                if (log.isDebugEnabled()) {
-                    log.debug(locusString + " -> " + locus);
-                }
-
-
                 if (locus != null) {
                     addRow(probeId, locus, values);
                 }
@@ -492,10 +409,6 @@ public class GCTParser extends AbstractParser {
 
     private void addRow(String probeId, Locus locus, float[] values) {
 
-        if (log.isDebugEnabled()) {
-            log.debug("Adding row for probe " + probeId + " to chromosome " + locus.getChr());
-        }
-
         List<Row> rows = rowMap.get(locus.getChr());
         if (rows == null) {
             rows = new ArrayList();
@@ -567,19 +480,13 @@ public class GCTParser extends AbstractParser {
         return names;
     }
 
-    private float[] getData(String heading, String chr) {
-
-        int columnIndex = this.headingIndexMap.get(heading);
-        List<Row> rows = rowMap.get(chr);
-
-        float[] data = new float[rows.size()];
-        for (int i = 0; i < rows.size(); i++) {
-            data[i] = rows.get(i).values[columnIndex];
-        }
+    private float[] getData(String chr, int row) {
+       List<Row> rows = rowMap.get(chr);
+        float[] data =  rows.get(row).values;
         return data;
-
     }
 
+
     /**
      * Search for a locus explicitly specified in the description field.
      * A locus can be specified either directrly, as a UCSC style locus string
@@ -629,7 +536,7 @@ public class GCTParser extends AbstractParser {
                 if (f == null) {
                     return null;
                 } else {
-                    return new Locus(f.getChromosome(), f.getStart(), f.getEnd());
+                    return new Locus(f.getChr(), f.getStart(), f.getEnd());
                 }
             }
 
@@ -637,7 +544,7 @@ public class GCTParser extends AbstractParser {
             else if (geneManager != null) {
                 Feature gene = FeatureDB.getFeature(locusString);
                 if (gene != null) {
-                    return new Locus(gene.getChromosome(), gene.getStart(), gene.getEnd());
+                    return new Locus(gene.getChr(), gene.getStart(), gene.getEnd());
                 }
             }
         }
@@ -696,4 +603,5 @@ public class GCTParser extends AbstractParser {
             this.values = values;
         }
     }
+
 }
diff --git a/src/org/broad/igv/tools/parsers/GCTtoIGVConverter.java b/src/org/broad/igv/tools/parsers/GCTtoIGVConverter.java
new file mode 100644
index 0000000..289b8dc
--- /dev/null
+++ b/src/org/broad/igv/tools/parsers/GCTtoIGVConverter.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.tools.parsers;
+
+import org.apache.log4j.Logger;
+import org.broad.igv.feature.Locus;
+
+import java.io.*;
+import java.util.List;
+
+
+/**
+ * @author jrobinso
+ * @date Oct 9, 2010
+ */
+public class GCTtoIGVConverter {
+
+    private static Logger log = Logger.getLogger(GCTtoIGVConverter.class);
+
+
+    /**
+     * Parse the file and output in ".igv" format
+     *
+     * @return
+     */
+    public static void convert(File inputFile, File outputFile, String probeResource, String genome) throws IOException {
+
+        GeneToLocusHelper locusHelper = new GeneToLocusHelper(probeResource, genome);
+
+
+        BufferedReader reader = null;
+        PrintWriter writer = null;
+
+        try {
+            reader = new BufferedReader(new FileReader(inputFile));
+            writer = new PrintWriter(new BufferedWriter(new FileWriter(outputFile)));
+
+            // Assume gene expression for now.
+            writer.println("#type=GENE_EXPRESSION");
+
+            // Skip first 2 rows
+            reader.readLine();
+            reader.readLine();
+
+            // Parse the header line
+            String headerLine = reader.readLine();
+            String[] tokens = headerLine.split("\t");
+
+            //The sample names in a GCT file start at column 2
+            writer.print("Chr\tStart\tEnd\tProbe");
+            for (int i = 2; i < tokens.length; i++) {
+                writer.print("\t" + tokens[i]);
+            }
+            writer.println();
+
+            String nextLine = null;
+            while ((nextLine = reader.readLine()) != null) {
+
+                // A gct row can map to multiple loci, normally this indicates a problem with the probe
+                DataRow row = new DataRow(nextLine);
+                String probe = row.getProbe();
+                List<Locus> loci = locusHelper.getLoci(probe, row.getDescription());
+                if (loci == null || loci.isEmpty()) {
+                    System.out.println("No locus found for: " + probe + "  " + row.getDescription());
+                } else {
+                    for (Locus locus : loci) {
+                        writer.print(locus.getChr() + "\t" + locus.getStart() + "\t" + locus.getEnd() + "\t" + probe );
+                        writer.println(row.getData());
+                    }
+                }
+
+            }
+        }
+        finally {
+            if (reader != null) {
+                reader.close();
+            }
+            if (writer != null) {
+                writer.close();
+            }
+        }
+
+    }
+
+    /**
+     * Represents a row of data from a GCT file.  Using this class if more effecient than tokeninzing the entire line.
+     * Some GCT files have over a thousand columns and we're only interested in the first 2
+     */
+    static class DataRow {
+        private String probe;
+        private String description;
+        private String data;
+
+        DataRow(String string) {
+            int firstTab = string.indexOf('\t');
+            int secondTab = string.indexOf('\t', firstTab + 1);
+
+            // TODO -- if either of the indeces firstTab or secondTab are < 0 throw an exception
+            probe = string.substring(0, firstTab);
+            description = string.substring(firstTab, secondTab);
+            data = string.substring(secondTab);
+        }
+
+        private String getProbe() {
+            return probe;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public String getData() {
+            return data;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/org/broad/igv/tools/parsers/GeneToLocusHelper.java b/src/org/broad/igv/tools/parsers/GeneToLocusHelper.java
new file mode 100644
index 0000000..fa0d755
--- /dev/null
+++ b/src/org/broad/igv/tools/parsers/GeneToLocusHelper.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.tools.parsers;
+
+import org.broad.igv.feature.*;
+import org.broad.igv.util.AsciiLineReader;
+import org.broad.igv.util.ParsingUtils;
+import org.broad.igv.util.ResourceLocator;
+
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @author jrobinso
+ * @date Oct 9, 2010
+ */
+public class GeneToLocusHelper {
+
+    static String LOCUS_START_DELIMITER = "|@";
+    static String LOCUS_END_DELIMITER = "|";
+
+    Map<String, Feature> probeLocusMap;
+    GeneManager geneManager = null;
+
+    public GeneToLocusHelper(String probeResource, String genome) throws IOException {
+        if (genome != null) {
+            geneManager = GeneManager.getGeneManager(genome);
+        }
+        if (probeResource != null && probeResource.trim().length() > 0) {
+            BEDFileParser parser = new BEDFileParser();
+            ResourceLocator rl = new ResourceLocator(probeResource);
+            AsciiLineReader reader = ParsingUtils.openAsciiReader(rl);
+            List<Feature> features = parser.loadFeatures(reader);
+            reader.close();
+            probeLocusMap = new HashMap(features.size() * 2);
+            for (Feature f : features) {
+                probeLocusMap.put(f.getName(), f);
+            }
+        }
+
+    }
+
+    public List<Locus> getLoci(String probeId, String description) {
+
+        // Search for locus in description string.  This relies on the special
+        // IGV convention for specifying loci  (e.g  |@chrX:1000-2000|
+        if ((description != null) && (description.length() > 3)) {
+            String[] locusStrings = getExplicitLocusStrings(description);
+            if (locusStrings != null) {
+
+                List<Locus> loci = new ArrayList(locusStrings.length);
+                for (String ls : locusStrings) {
+                    ls = ls.trim();
+                    Locus locus = getLocus(ls);
+                    if ((locus != null) && locus.isValid()) {
+                        loci.add(locus);
+                    }
+                }
+                return loci;
+            }
+
+
+            // Search for locus from the probe name itself.
+            Locus locus = getLocus(probeId);
+            if ((locus != null) && locus.isValid()) {
+                return Arrays.asList(locus);
+            }
+        }
+
+        // See if the probes can be mapped to genes
+
+        String[] genes = ProbeToGeneMap.getInstance().getGenesForProbe(probeId);
+        if (genes != null) {
+            List<Locus> loci = new ArrayList(genes.length);
+            for (String g : genes) {
+                Locus locus = getLocus(g);
+                if (locus != null) {
+                    loci.add(locus);
+                }
+            }
+            return loci;
+        }
+        return null;
+    }
+
+
+    /**
+     * Search for a locus explicitly specified in the description field.
+     * A locus can be specified either directrly, as a UCSC style locus string
+     * (e.g. chrX:1000-2000), or indirectly as a HUGO gene symbol (e.g. egfr).
+     * The locus string is distinguished by the  delimiters |@ and |.
+     *
+     */
+    private String[] getExplicitLocusStrings(String description) {
+
+        // Search for locus in description string
+        int startIndex = description.indexOf(LOCUS_START_DELIMITER);
+        if (startIndex < 0) {
+            return null;
+        } else {
+            startIndex += 2;
+        }
+
+        int endIndex = description.indexOf(LOCUS_END_DELIMITER, startIndex + 1);
+        if (endIndex < 0) {
+
+            // Assume the locus extends to the end of the string
+            endIndex = description.length();
+        }
+
+        if (endIndex > startIndex + 3) {
+            String locusString = description.substring(startIndex, endIndex);
+            if (locusString.contains(",")) {
+                return locusString.split(",");
+            } else {
+                return new String[]{locusString};
+            }
+        }
+        return null;
+
+    }
+
+    /**
+     * Return a locus from the gene (e.g. EGFR) or locus (e.g. chr1:1-100) string
+     *
+     *
+     */
+    Locus getLocus(String geneOrLocusString) {
+        Locus locus = new Locus(geneOrLocusString);
+        if (locus.isValid()) {
+            return locus;
+        } else {
+
+            if (probeLocusMap != null) {
+                Feature f = probeLocusMap.get(geneOrLocusString);
+                if (f == null) {
+                    return null;
+                } else {
+                    return new Locus(f.getChr(), f.getStart(), f.getEnd());
+                }
+            }
+
+            // Maybe its a gene
+            else if (geneManager != null) {
+                Feature gene = FeatureDB.getFeature(geneOrLocusString);
+                if (gene != null) {
+                    return new Locus(gene.getChr(), gene.getStart(), gene.getEnd());
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/src/org/broad/igv/tools/parsers/UnsortedException.java b/src/org/broad/igv/tools/parsers/UnsortedException.java
index 46a547b..1fe0b35 100644
--- a/src/org/broad/igv/tools/parsers/UnsortedException.java
+++ b/src/org/broad/igv/tools/parsers/UnsortedException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/tools/parsers/WiggleParser.java b/src/org/broad/igv/tools/parsers/WiggleParser.java
index 4bbd3b8..ca23ca3 100644
--- a/src/org/broad/igv/tools/parsers/WiggleParser.java
+++ b/src/org/broad/igv/tools/parsers/WiggleParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -19,12 +19,12 @@ package org.broad.igv.tools.parsers;
 
 //~--- non-JDK imports --------------------------------------------------------
 
-import org.broad.igv.util.ParsingUtils;
+import org.broad.igv.feature.Genome;
 import org.broad.igv.track.TrackProperties;
 import org.broad.igv.track.TrackType;
 import org.broad.igv.util.AsciiLineReader;
+import org.broad.igv.util.ParsingUtils;
 import org.broad.igv.util.ResourceLocator;
-import org.broad.igv.feature.Genome;
 
 import java.io.IOException;
 import java.util.HashSet;
@@ -82,12 +82,12 @@ public class WiggleParser {
 
         parseHeader();
 
-        String[] trackNames = {locator.getDisplayName()};
+        String[] trackNames = {locator.getTrackName()};
 
         // TODO -- total hack to get Manuel's file parsed quickly.  Revisit (obviously);
         if (locator.getPath().endsWith(".ewig") || locator.getPath().endsWith(".ewig.gz")) {
             trackNames = new String[5];
-            trackNames[4] = locator.getDisplayName();
+            trackNames[4] = locator.getTrackName();
             trackNames[0] = "A";
             trackNames[1] = "C";
             trackNames[2] = "G";
@@ -260,7 +260,7 @@ public class WiggleParser {
                                     dataArray = new float[nTokens - 3];
                                 }
 
-                                chr = genome.getChromosomeAlias (tokens[0].trim());
+                                chr = (genome == null ? tokens[0].trim() : genome.getChromosomeAlias(tokens[0].trim()));
                                 if (!chr.equals(lastChr)) {
                                     newChromosome();
                                 }
@@ -296,7 +296,7 @@ public class WiggleParser {
                                 }
                                 lastPosition = startPosition;
 
-                                int endPosition = startPosition + span - startBase;
+                                int endPosition = startPosition + span;
 
                                 for (int i = 0; i < dataArray.length; i++) {
                                     dataArray[i] = Float.parseFloat(tokens[1 + i].trim());
@@ -347,12 +347,12 @@ public class WiggleParser {
 
     // fixedStep chrom=chrM strt=1 step=1
     private void parseStepLine(String header) {
-        String[] tokens = header.split(" ");
+        String[] tokens = header.split("\\s+");
         for (String token : tokens) {
             String[] keyValue = token.split("=");
             if (keyValue.length >= 2) {
                 if (keyValue[0].equalsIgnoreCase("chrom")) {
-                    chr  = genome.getChromosomeAlias (keyValue[1]);
+                    chr = (genome == null ? keyValue[1] : genome.getChromosomeAlias(keyValue[1]));
                     if (!chr.equals(lastChr)) {
                         newChromosome();
                     }
diff --git a/src/org/broad/igv/tools/sort/BedSorter.java b/src/org/broad/igv/tools/sort/BedSorter.java
index 7410f3b..9d68523 100644
--- a/src/org/broad/igv/tools/sort/BedSorter.java
+++ b/src/org/broad/igv/tools/sort/BedSorter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -27,23 +27,36 @@ import net.sf.samtools.util.AsciiLineReader;
 import java.io.File;
 import java.io.PrintWriter;
 
+
+
 /**
  * @author jrobinso
  */
 public class BedSorter extends Sorter {
+    private int chrCol;
+    private int startCol;
+
 
     public BedSorter(File inputFile, File outputFile) {
         super(inputFile, outputFile);
+        if (inputFile.getName().endsWith(".psl") || inputFile.getName().endsWith(".pslx")) {
+            chrCol = 13;
+            startCol = 15;
+        } else {
+            chrCol = 0;
+            startCol = 1;
+        }
     }
 
     @Override
     Parser getParser() {
-        return new Parser(0, 1);
+        return new Parser(chrCol, startCol);
     }
 
     @Override
     String writeHeader(AsciiLineReader reader, PrintWriter writer) {
         String nextLine = reader.readLine();
+        // TODO -- check "browser" line syntax,  is it a multi-line directive?
         while (nextLine.startsWith("#") ||
                 nextLine.startsWith("browser") ||
                 nextLine.startsWith("track") ||
@@ -57,3 +70,4 @@ public class BedSorter extends Sorter {
 
     }
 }
+
diff --git a/src/org/broad/igv/tools/sort/CNSorter.java b/src/org/broad/igv/tools/sort/CNSorter.java
index 93df9f2..a02edc9 100644
--- a/src/org/broad/igv/tools/sort/CNSorter.java
+++ b/src/org/broad/igv/tools/sort/CNSorter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/tools/sort/GFFSorter.java b/src/org/broad/igv/tools/sort/GFFSorter.java
new file mode 100644
index 0000000..a261885
--- /dev/null
+++ b/src/org/broad/igv/tools/sort/GFFSorter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.tools.sort;
+
+import net.sf.samtools.util.AsciiLineReader;
+
+import java.io.File;
+import java.io.PrintWriter;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: Jun 28, 2010
+ * Time: 2:37:24 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class GFFSorter extends Sorter {
+
+    public GFFSorter(File inputFile, File outputFile) {
+        super(inputFile, outputFile);
+    }
+
+    @Override
+    Parser getParser() {
+        return new Parser(0, 3);
+    }
+
+    @Override
+    String writeHeader(AsciiLineReader reader, PrintWriter writer) {
+        String nextLine = reader.readLine();
+        while (nextLine.startsWith("#")) {
+            writer.println(nextLine);
+            nextLine = reader.readLine();
+        }
+
+        return nextLine;
+    }
+}
diff --git a/src/org/broad/igv/tools/sort/Parser.java b/src/org/broad/igv/tools/sort/Parser.java
index 79326af..c12ef12 100644
--- a/src/org/broad/igv/tools/sort/Parser.java
+++ b/src/org/broad/igv/tools/sort/Parser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -50,7 +50,14 @@ public class Parser {
         // TODO -- what to do if nTokens < startCol?
 
         String chr = fields[chrCol];
-        int start = Integer.parseInt(fields[startCol].trim());
+
+        int start ;
+        try {
+            start = Integer.parseInt(fields[startCol].trim());
+        }
+        catch(NumberFormatException e) {
+            start = Integer.MAX_VALUE;
+        }
         String text = nextLine;
 
         return new SortableRecord(chr, start, text);
diff --git a/src/org/broad/igv/tools/sort/SAMSorter.java b/src/org/broad/igv/tools/sort/SAMSorter.java
index beb012c..5702c80 100644
--- a/src/org/broad/igv/tools/sort/SAMSorter.java
+++ b/src/org/broad/igv/tools/sort/SAMSorter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/tools/sort/SortableRecord.java b/src/org/broad/igv/tools/sort/SortableRecord.java
index c94a8f4..69cd277 100644
--- a/src/org/broad/igv/tools/sort/SortableRecord.java
+++ b/src/org/broad/igv/tools/sort/SortableRecord.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/tools/sort/SortableRecordCodec.java b/src/org/broad/igv/tools/sort/SortableRecordCodec.java
index 7ba9b20..3dc6f56 100644
--- a/src/org/broad/igv/tools/sort/SortableRecordCodec.java
+++ b/src/org/broad/igv/tools/sort/SortableRecordCodec.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -24,11 +24,10 @@ import org.apache.log4j.Logger;
 import java.io.*;
 
 /**
- * Created by IntelliJ IDEA.
- * User: nazaire
- * Date: Jun 2, 2009
+ * Codec for Picard sorting classes.   Used to serialize and deserialize records to disk.
  */
 public class SortableRecordCodec implements SortingCollection.Codec<SortableRecord> {
+
     private static Logger log = Logger.getLogger(SortableRecordCodec.class);
     DataOutputStream outputStream;
     DataInputStream inputStream;
@@ -41,11 +40,11 @@ public class SortableRecordCodec implements SortingCollection.Codec<SortableReco
         this.inputStream = new DataInputStream(inputStream);
     }
 
-    public void encode(SortableRecord alignment) {
+    public void encode(SortableRecord record) {
         try {
-            outputStream.writeUTF(alignment.getChromosome());
-            outputStream.writeInt(alignment.getStart());
-            outputStream.writeUTF(alignment.getText());
+            outputStream.writeUTF(record.getChromosome());
+            outputStream.writeInt(record.getStart());
+            outputStream.writeUTF(record.getText());
         } catch (IOException ex) {
             log.error("Error encoding alignment", ex);
         }
diff --git a/src/org/broad/igv/tools/sort/SortedTxtSorter.java b/src/org/broad/igv/tools/sort/SortedTxtSorter.java
new file mode 100644
index 0000000..097f72e
--- /dev/null
+++ b/src/org/broad/igv/tools/sort/SortedTxtSorter.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.tools.sort;
+
+import net.sf.samtools.util.AsciiLineReader;
+
+import java.io.File;
+import java.io.PrintWriter;
+
+/**
+ * Sorter for a "sorted.txt" file.  Normally this is not needed, as these files are sorted.  This is provided for the
+ * case where several such files are contatenated.
+ *
+ */
+public class SortedTxtSorter extends Sorter {
+
+    public SortedTxtSorter(File inputFile, File outputFile) {
+        super(inputFile, outputFile);
+    }
+
+    @Override
+    Parser getParser() {
+       return new Parser(10, 12);
+    }
+
+    @Override
+    String writeHeader(AsciiLineReader reader, PrintWriter writer) {
+        return null; 
+    }
+}
diff --git a/src/org/broad/igv/tools/sort/Sorter.java b/src/org/broad/igv/tools/sort/Sorter.java
index 47cee4d..6b22d9d 100644
--- a/src/org/broad/igv/tools/sort/Sorter.java
+++ b/src/org/broad/igv/tools/sort/Sorter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,6 +22,9 @@ import jargs.gnu.CmdLineParser;
 import net.sf.samtools.util.AsciiLineReader;
 import net.sf.samtools.util.CloseableIterator;
 import net.sf.samtools.util.SortingCollection;
+import org.broad.igv.tools.CommandLineStatusMonitor;
+import org.broad.igv.tools.PreprocessingException;
+import org.broad.igv.tools.StatusMonitor;
 
 import java.io.*;
 import java.util.Comparator;
@@ -33,11 +36,13 @@ import java.util.Comparator;
  */
 public abstract class Sorter {
 
-    static int MAX_RECORDS_IN_RAM = 100000;
+    static int MAX_RECORDS_IN_RAM = 500000;
     File inputFile;
     File outputFile;
+    private int maxRecords;
     private File tmpDir;
     static final String usageString = "igvtools sort <inputFile> [outputFile]";
+    StatusMonitor statusMonitor = new CommandLineStatusMonitor();
 
     public static Sorter getSorter(String[] argv) {
 
@@ -50,6 +55,7 @@ public abstract class Sorter {
 
         CmdLineParser parser = new CmdLineParser();
         CmdLineParser.Option tmpDirOption = parser.addStringOption('t', "tmpDir");
+        CmdLineParser.Option maxRecordsOption = parser.addStringOption('m', "maxRecords");
 
         try {
             parser.parse(argv);
@@ -78,6 +84,20 @@ public abstract class Sorter {
             sorter.setTmpDir(tmpDir);
         }
 
+        int mr = MAX_RECORDS_IN_RAM;
+        String maxRecordsString = (String) parser.getOptionValue(maxRecordsOption);
+        if (maxRecordsOption != null) {
+            try {
+                mr = Integer.parseInt(maxRecordsString);
+            }
+            catch (NumberFormatException e) {
+                System.out.println("Warning: max records is not an integer: (" + maxRecordsString + ").  Setting" +
+                        "max records to " + MAX_RECORDS_IN_RAM);
+                mr = MAX_RECORDS_IN_RAM;
+            }
+        }
+
+        sorter.setMaxRecords(mr);
         return sorter;
     }
 
@@ -92,11 +112,17 @@ public abstract class Sorter {
             return new SAMSorter(inputFile, outputFile);
         } else if (shortFN.endsWith(".aligned") || shortFN.endsWith(".bed")) {
             return new BedSorter(inputFile, outputFile);
-        } else {
-            System.out.println("Unknown file type or sorting not supported for: " + inputFile.getName());
-            return null;
+        } else if (shortFN.endsWith(".sorted")) {
+            return new SortedTxtSorter(inputFile, outputFile);
+        } else if (shortFN.endsWith(".gff") || shortFN.endsWith(".gff3")) {
+            return new GFFSorter(inputFile, outputFile);
+        } else if (shortFN.endsWith(".vcf")) {
+            return new VCFSorter(inputFile, outputFile);
+        } else if (shortFN.endsWith(".psl") || shortFN.endsWith(".pslx")) {
+            return new BedSorter(inputFile, outputFile);
+        }else {
+            throw new PreprocessingException("Unknown file type or sorting not supported for: " + inputFile.getName());
         }
-
     }
 
     public Sorter(File inputFile, File outputFile) {
@@ -152,8 +178,7 @@ public abstract class Sorter {
             };
 
 
-            SortingCollection cltn = SortingCollection.newInstance(SortableRecord.class,
-                    codec, comp, MAX_RECORDS_IN_RAM, tmpDir);
+            SortingCollection cltn = SortingCollection.newInstance(SortableRecord.class, codec, comp, maxRecords, tmpDir);
 
             Parser parser = getParser();
             AsciiLineReader reader = new AsciiLineReader(fis);
@@ -172,7 +197,7 @@ public abstract class Sorter {
             CloseableIterator<SortableRecord> iter = cltn.iterator();
             while (iter.hasNext()) {
                 SortableRecord al = iter.next();
-                writer.println(al.getText() + "\t");
+                writer.println(al.getText());
 
             }
             iter.close();
@@ -197,4 +222,8 @@ public abstract class Sorter {
     public void setTmpDir(File tmpDir) {
         this.tmpDir = tmpDir;
     }
+
+    public void setMaxRecords(int maxRecords) {
+        this.maxRecords = maxRecords;
+    }
 }
diff --git a/src/org/broad/igv/tools/sort/SorterTest.java b/src/org/broad/igv/tools/sort/SorterTest.java
index b8e771d..c4ac01a 100644
--- a/src/org/broad/igv/tools/sort/SorterTest.java
+++ b/src/org/broad/igv/tools/sort/SorterTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/tools/sort/VCFSorter.java b/src/org/broad/igv/tools/sort/VCFSorter.java
new file mode 100644
index 0000000..3ee9ec4
--- /dev/null
+++ b/src/org/broad/igv/tools/sort/VCFSorter.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.tools.sort;
+
+import net.sf.samtools.util.AsciiLineReader;
+
+import java.io.File;
+import java.io.PrintWriter;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: Jun 28, 2010
+ * Time: 2:46:12 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class VCFSorter extends Sorter {
+
+    public VCFSorter(File inputFile, File outputFile) {
+        super(inputFile, outputFile);
+    }
+
+    @Override
+    Parser getParser() {
+        return new Parser(0, 1);
+    }
+
+    @Override
+    String writeHeader(AsciiLineReader reader, PrintWriter writer) {
+        String nextLine = reader.readLine();
+        // TODO -- check "browser" line syntax,  is it a multi-line directive?
+        while (nextLine.startsWith("#")) {
+            writer.println(nextLine);
+            nextLine = reader.readLine();
+        }
+
+        return nextLine;
+
+
+    }
+}
diff --git a/src/org/broad/igv/track/AbstractFeatureTrack.java b/src/org/broad/igv/track/AbstractFeatureTrack.java
deleted file mode 100644
index 9f9ca0e..0000000
--- a/src/org/broad/igv/track/AbstractFeatureTrack.java
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-package org.broad.igv.track;
-
-import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
-import org.broad.igv.PreferenceManager;
-import org.broad.igv.feature.Feature;
-import org.broad.igv.feature.Genome;
-import org.broad.igv.renderer.BasicFeatureRenderer;
-import org.broad.igv.renderer.FeatureRenderer;
-import org.broad.igv.ui.IGVMainFrame;
-import org.broad.igv.ui.IGVModel;
-import org.broad.igv.util.ResourceLocator;
-
-import java.awt.*;
-import java.util.ArrayList;
-import java.util.LinkedHashSet;
-import java.util.List;
-
-/**
- * @author jrobinso
- */
-public abstract class AbstractFeatureTrack extends AbstractTrack {
-
-    private static Logger log = Logger.getLogger(AbstractFeatureTrack.class);
-    Rectangle expCollapse;
-    /**
-     * Allocate features to multiple "levels" for expanded mode.  In this mode features at each
-     * level do not overlap.
-     * <p/>
-     * Assumption:  features are sorted by increasing start location.
-     *
-     * @param features
-     * @return
-     */
-    // max # of levels
-    static int maxLevels = 200;
-    boolean expanded;
-    List<Rectangle> featureRects = new ArrayList();
-    private String lastChromosomeName;
-    private List<List<Feature>> levelList;
-    /**
-     * A hack until tracks can be refactored.
-     */
-    private boolean mutationTrack = false;
-    private FeatureRenderer renderer;
-
-    public AbstractFeatureTrack(ResourceLocator locator, String name) {
-        super(locator, name);
-
-        expanded = PreferenceManager.getInstance().isExpandTracks();
-    }
-
-    private void calculateLevels(List<Feature> features) {
-
-        int currentLevelCount = this.getNumberOfFeatureLevels();
-        levelList = new ArrayList<List<Feature>>();
-        if (features.isEmpty()) {
-            return;
-        }
-        LinkedHashSet<Feature> featuresToAllocate = new LinkedHashSet();
-        featuresToAllocate.addAll(features);
-        while (!featuresToAllocate.isEmpty() && levelList.size() < maxLevels) {
-            List<Feature> nextLevel = new ArrayList();
-            levelList.add(nextLevel);
-            int lastEnd = -1;
-            for (Feature feature : featuresToAllocate) {
-                if (feature.getStart() > lastEnd) {
-                    nextLevel.add(feature);
-                    lastEnd = feature.getEnd();
-                }
-            }
-            if (nextLevel.isEmpty()) {
-                System.out.println("");
-            }
-            featuresToAllocate.removeAll(nextLevel);
-        }
-
-        // This dependency on IGVMainFrame here is unfortunate.  TODO -- refactor ths to
-        // use event/listener ?
-        if (levelList.size() != currentLevelCount) {
-            IGVMainFrame.getInstance().doRefresh();
-        }
-    }
-
-    /**
-     * @return
-     * @deprecated
-     */
-    public double getDataMax() {
-        return 0;
-    }
-
-    public List<Feature> getFeatures(String chr, int startLocation, int endLocation) {
-        return getFeatures(chr);
-    }
-
-
-    public synchronized List<List<Feature>> getFeaturesByLevels(String chr, int start, int end) {
-        if ((levelList == null) || (lastChromosomeName == null) || !lastChromosomeName.equals(chr)) {
-            lastChromosomeName = chr;
-            if (!isMultiLevel()) {
-                levelList = new ArrayList();
-                List<Feature> features = getFeatures(chr, start, end);
-                if (features != null) {
-                    levelList.add(features);
-                }
-            } else {
-
-                List<Feature> features = getFeatures(chr, start, end);
-                if (features != null) {
-                    calculateLevels(features);
-                }
-            }
-        }
-        return levelList;
-    }
-
-    @Override
-    public int getHeight() {
-
-        if (false == isVisible()) {
-            return 0;
-        }
-
-        int levelCount = 1;
-        if ((levelList != null) && !levelList.isEmpty()) {
-            levelCount = levelList.size();
-        }
-
-        return super.getHeight() * levelCount;
-    }
-
-    public double getMedian(String stat) {
-        return 1.0;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public int getNumberOfFeatureLevels() {
-
-        if ((levelList == null) || levelList.isEmpty()) {
-            return 1;
-        } else {
-            return levelList.size();
-        }
-    }
-
-
-    // Define the region score to be simply the # of features over the interval
-    public float getRegionScore(String chr, int start, int end, int zoom, RegionScoreType type) {
-        if (type == RegionScoreType.SCORE) {
-            List<Feature> flist = getFeatures(chr, start, end);
-            if (flist != null) {
-                int count = 0;
-                for (Feature f : flist) {
-                    if (f.getStart() <= end && f.getEnd() >= start) {
-                        count++;
-                    } else if (f.getStart() > end) {
-                        break;
-                    }
-                }
-                return count;
-            }
-        }
-        return -Float.MAX_VALUE;
-
-    }
-
-
-    public FeatureRenderer getRenderer() {
-        if (renderer == null) {
-            setRendererClass(BasicFeatureRenderer.class);
-        }
-        return renderer;
-    }
-
-    public String getValueStringAt(String chr, double position, int y) {
-
-        // Determine the level number (for expanded tracks.
-        int levelNumber = 0;
-        if (isMultiLevel() && (this.featureRects != null)) {
-            for (int i = 0; i < featureRects.size(); i++) {
-                Rectangle r = featureRects.get(i);
-                if ((y >= r.y) && (y <= r.getMaxY())) {
-                    levelNumber = i;
-                    break;
-                }
-            }
-        }
-
-        int nLevels = this.getNumberOfFeatureLevels();
-        List<Feature> features = null;
-        if ((nLevels > 1) && (levelNumber < nLevels)) {
-            features = this.getFeaturesByLevels(chr, (int) position - 10, (int) position + 10).get(levelNumber);
-        } else {
-            features = getFeatures(chr, (int) position - 10, (int) position + 10);
-        }
-        if (features == null) {
-            return null;
-        }
-
-        // give a 2 pixel window, otherwise very narrow features will be missed.
-        double bpPerPixel = IGVModel.getInstance().getViewContext().getScale();
-        double minWidth = 2 * bpPerPixel;
-        Feature feature = (Feature) getFeatureAt(position, minWidth, features);
-        return (feature == null) ? null : feature.getValueString(position, getWindowFunction());
-    }
-
-    public WindowFunction getWindowFunction() {
-        return WindowFunction.count;
-    }
-
-    /**
-     * Method description
-     *
-     * @param x
-     * @param y
-     * @return
-     */
-    public boolean handleClick(int x, int y) {
-
-        /*
-         * if (expCollapse.contains(x, y))
-         * {
-         *   setExpanded(!isExpanded());
-         *   IGVMainFrame.getInstance().repaintDataPanels();
-         *   return true;
-         * }
-         */
-        return false;
-    }
-
-    @Override
-    public boolean isExpanded() {
-        return expanded;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public boolean isLogNormalized() {
-        return true;
-    }
-
-    /**
-     * Return true if the track has multiple levels.  By default this is synonomous with an expanded
-     * state.  Subclasses that have multiple levels in the collapsed state can override.
-     *
-     * @return
-     */
-    public boolean isMultiLevel() {
-        return isExpanded();
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public boolean isMutationTrack() {
-        return mutationTrack;
-    }
-
-    public void overlay(RenderContext context, Rectangle rect) {
-        getRenderer().setOverlayMode(true);
-        renderFeatures(context, rect);
-    }
-
-    abstract public List<Feature> getFeatures(String chr);
-
-    abstract public void setFeatures(String chr, List<Feature> features);
-
-    private void recalculateLevels() {
-        String chr = IGVModel.getInstance().getViewContext().getChrName();
-        List<Feature> tmp = this.getFeatures(chr);
-        if (tmp != null) {
-            calculateLevels(tmp);
-        }
-    }
-
-    public void render(RenderContext context, Rectangle rect) {
-        getRenderer().setOverlayMode(false);
-        Rectangle renderRect = new Rectangle(rect);
-        int margin = Math.min(5, (int) (rect.height / 10.0));
-        renderRect.y = renderRect.y + margin;
-        renderFeatures(context, renderRect);
-    }
-
-    public void renderFeatures(RenderContext context, Rectangle inputRect) {
-        try {
-            List<List<Feature>> featureLists = getFeaturesByLevels(context.getChr(), (int) context.getStartLocation(), (int) context.getEndLocation());
-
-            if (featureLists == null || featureLists.isEmpty()) {
-                return;
-            }
-
-            int nLevels = featureLists.size();
-            synchronized (featureRects) {
-
-                featureRects.clear();
-
-
-                // Divide rectangle into equal height levels
-                double y = inputRect.getY();
-                double h = inputRect.getHeight() / nLevels;
-                int levelNumber = 0;
-                for (List<Feature> features : featureLists) {
-                    Rectangle rect = new Rectangle(inputRect.x, (int) y, inputRect.width, (int) h);
-                    featureRects.add(rect);
-                    getRenderer().renderFeatures(features, context, rect, this);
-                    y += h;
-                    levelNumber++;
-                }
-            }
-        } catch (Exception ex) {
-            log.error("Error rendering track", ex);
-            throw new RuntimeException("Error rendering track ", ex);
-        }
-    }
-
-    protected void sampleGenomeFeatures() {
-        List<Feature> chrAllFeatures = new ArrayList(1000);
-        Genome currentGenome = IGVModel.getInstance().getViewContext().getGenome();
-        int sampleLength = (int) ((double) currentGenome.getLength() / (1000 * 700));
-        int lastFeaturePosition = -1;
-        for (String chr : currentGenome.getChromosomeNames()) {
-            List<Feature> features = getFeatures(chr);
-            if (features != null) {
-                long offset = currentGenome.getCumulativeOffset(chr);
-                for (Feature f : features) {
-                    int genStart = (int) ((offset + f.getStart()) / 1000);
-                    int genEnd = (int) ((offset + f.getEnd()) / 1000);
-                    if (genEnd > lastFeaturePosition + sampleLength) {
-                        Feature f2 = (Feature) f.copy();
-                        //BasicFeature f2 = new BasicFeature(IGVConstants.CHR_ALL, genStart, genEnd, f.getStrand());
-                        f2.setChromosome(IGVConstants.CHR_ALL);
-                        f2.setStart(genStart);
-                        f2.setEnd(genEnd);
-                        f2.setName(f.getName());
-                        chrAllFeatures.add(f2);
-
-                        lastFeaturePosition = genEnd;
-                    }
-                }
-            }
-        }
-
-        setFeatures(IGVConstants.CHR_ALL, chrAllFeatures);
-    }
-
-    public void setChromosome(String chr) {
-    }
-
-    protected void resetLevelList() {
-        levelList = null;
-    }
-
-    /**
-     * Method description
-     *
-     * @param max
-     */
-    public void setDataMax(float max) {
-    }
-
-    @Override
-    public void setExpanded(boolean value) {
-        if (value != expanded) {
-            expanded = value;
-            if (expanded) {
-                recalculateLevels();
-            } else {
-                levelList = null;
-            }
-        }
-    }
-
-    @Override
-    public void setHeight(int newHeight) {
-
-        int levelCount = 1;
-        if ((levelList != null) && !levelList.isEmpty()) {
-            levelCount = levelList.size();
-        }
-
-        super.setHeight(Math.max(getMinimumHeight(), newHeight / levelCount));
-    }
-
-    /**
-     * Method description
-     *
-     * @param mutationTrack
-     */
-    public void setMutationTrack(boolean mutationTrack) {
-        this.mutationTrack = mutationTrack;
-    }
-
-    public void setRendererClass(Class rc) {
-        try {
-            renderer = (FeatureRenderer) rc.newInstance();
-        } catch (Exception ex) {
-            log.error("Error instatiating renderer ", ex);
-        }
-    }
-
-    public void setStatType(WindowFunction type) {
-    }
-
-    /**
-     * Method description
-     *
-     * @param zoom
-     */
-    public void setZoom(int zoom) {
-    }
-}
-
diff --git a/src/org/broad/igv/track/AbstractTrack.java b/src/org/broad/igv/track/AbstractTrack.java
index 102de6f..2f52c7d 100644
--- a/src/org/broad/igv/track/AbstractTrack.java
+++ b/src/org/broad/igv/track/AbstractTrack.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -20,15 +20,16 @@ package org.broad.igv.track;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
 import org.broad.igv.PreferenceManager;
+import org.broad.igv.feature.Feature;
 import org.broad.igv.feature.FeatureUtils;
 import org.broad.igv.feature.LocusScore;
 import org.broad.igv.renderer.*;
-import org.broad.igv.session.SessionManager;
+import org.broad.igv.session.SessionReader;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.ui.FontManager;
-import org.broad.igv.ui.IGVModel;
-import org.broad.igv.ui.ViewContext;
+import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.UIConstants;
 import org.broad.igv.util.ColorUtilities;
 import org.broad.igv.util.ResourceLocator;
 
@@ -61,6 +62,7 @@ public abstract class AbstractTrack implements Track {
     static {
         defaultRendererMap.put(TrackType.RNAI, HeatmapRenderer.class);
         defaultRendererMap.put(TrackType.COPY_NUMBER, HeatmapRenderer.class);
+        defaultRendererMap.put(TrackType.CNV, HeatmapRenderer.class);
         defaultRendererMap.put(TrackType.ALLELE_SPECIFIC_COPY_NUMBER, HeatmapRenderer.class);
         defaultRendererMap.put(TrackType.GENE_EXPRESSION, HeatmapRenderer.class);
         defaultRendererMap.put(TrackType.DNA_METHYLATION, HeatmapRenderer.class);
@@ -69,16 +71,29 @@ public abstract class AbstractTrack implements Track {
         defaultRendererMap.put(TrackType.CHIP_CHIP, HeatmapRenderer.class);
     }
 
-    /**
-     * User supplied track name.  For data this is usually the sample name, or column
-     * heading from an input file.
-     */
+
     private String id;
+    private String id_142;    // 1.4.2 name, used to restore old sessions.
     private String name;
-    /**
-     * Name for display.  Can be null, in which case "name" is used.
-     */
-    private String displayName;
+    private Color midColor;
+    private String url;
+    private boolean itemRGB = true;
+    private boolean useScore;
+    private float viewLimitMin;
+    private float viewLimitMax;
+    private int fontSize = 9;
+    private boolean showDataRange = true;
+    private String sampleId;
+
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
     /**
      * The source file from which this track was created.  Defaults to an
      * empty string, which indicates that the track was not derived from a
@@ -99,9 +114,7 @@ public abstract class AbstractTrack implements Track {
     private ResourceLocator resourceLocator;
 
     private TrackType trackType = TrackType.OTHER;
-    //private Color posColor;
-    //private Color altColor;
-    //private Color midColor = Color.WHITE;
+
     /**
      * Continuous posColor scale for this track
      *
@@ -140,151 +153,107 @@ public abstract class AbstractTrack implements Track {
      * Constructs ...
      *
      * @param dataResourceLocator
-     * @param name
      */
-    public AbstractTrack(ResourceLocator dataResourceLocator, String name) {
-        this.overlayVisible = PreferenceManager.getInstance().getDiplayOverlayTracks();
+    public AbstractTrack(
+            ResourceLocator dataResourceLocator,
+            String id,
+            String name) {
         this.resourceLocator = dataResourceLocator;
+        this.id = id;
         this.name = name;
-        this.id = String.valueOf(name == null ? dataResourceLocator.getPath() : name);
+        init();
     }
 
+    public AbstractTrack(ResourceLocator dataResourceLocator) {
+        this.resourceLocator = dataResourceLocator;
+        this.id = dataResourceLocator.getPath();
 
-    public AbstractTrack(String name) {
-        this.overlayVisible = PreferenceManager.getInstance().getDiplayOverlayTracks();
-        this.name = name;
-        this.id = name;
+        String drName = dataResourceLocator.getName();
+        this.name = drName != null ? drName : dataResourceLocator.getFileName();
+
+
+        init();
     }
 
 
-    public AbstractTrack(String id, String displayName) {
-        this.overlayVisible = PreferenceManager.getInstance().getDiplayOverlayTracks();
-        this.displayName = displayName;
+    public AbstractTrack(String id) {
         this.name = id;
         this.id = id;
-    }
+        init();
 
-    /**
-     * Ignored by default.  Subclases should override as required
-     */
-    public void preloadData(String chr, int start, int end, int zoom) {
     }
 
-    /**
-     * Update track properties from managed attributes.  This method should be called when
-     * attributes are loaded.
-     */
-    public void updateProperties() {
-        Map<String, String> props = AttributeManager.getInstance().getAllAttributes(id);
-        if (props != null) {
-            try {
-                String colorString = props.get(COLOR_ATTR_KEY);
-                if (colorString != null) {
-                    Color trackColor = this.getColorAttribute(COLOR_ATTR_KEY, null);
-                    if (trackColor != null) {
-                        posColor = trackColor;
-                        ContinuousColorScale cs = getColorScale();
-                        if (cs != null) {
-                            cs.setMaxColor(trackColor);
-                        }
-                    }
-                }
 
-                if (colorString != null) {
-                    Color altColor = this.getColorAttribute(ALT_COLOR_ATTR_KEY, null);
-                    if (altColor != null) {
-                        setAltColor(altColor);
-                        ContinuousColorScale cs = getColorScale();
-                        if (cs != null) {
-                            cs.setMinColor(altColor);
-                        }
-                    }
-                }
+    public AbstractTrack(String id, String name) {
+        this.name = name;
+        this.id = id;
+        init();
+    }
 
-                String heightString = props.get(HEIGHT_ATTR_KEY);
-                if (heightString != null) {
-                    try {
-                        int h = Integer.parseInt(heightString);
-                        if (h > 0) {
-                            setHeight(h);
-                        }
-
-                    } catch (NumberFormatException numberFormatException) {
-                        log.info("Unrecognized integer string: " + heightString);
-                    }
-                }
-                String rendererString = props.get(RENDERER_ATTR_KEY);
-                if (rendererString != null) {
-                    Class rendererClass = RendererFactory.getRendererClass(rendererString.toUpperCase());
-                    if (rendererClass != null) {
-                        setRendererClass(rendererClass);
-                    }
-                }
-                String minString = props.get(DATA_MIN_ATTR_KEY);
-                String baseString = props.get(DATA_MID_ATTR_KEY);
-                String maxString = props.get(DATA_MAX_ATTR_KEY);
-                if (maxString != null) {
-                    try {
-                        float min = (minString == null) ? 0 : Float.parseFloat(minString);
-                        float max = Float.parseFloat(maxString);
-                        float base = min;
-                        if (baseString != null) {
-                            base = Float.parseFloat(baseString);
-                        }
-                        setDataRange(new DataRange(min, base, max));
-                    } catch (NumberFormatException e) {
-                        log.error("Error parsing float for axis definition. ", e);
-                    }
+    private void init() {
+        overlayVisible = IGVMainFrame.getInstance().getSession().getDisplayOverlayTracks();
+        showDataRange = PreferenceManager.getInstance().getChartPreferences().isShowDataRange();
+        this.id_142 = String.valueOf(name == null ? resourceLocator.getPath() : name);
 
-                }
+    }
 
-            } catch (Exception e) {
-                log.error("Error updating track properties: ", e);
-            }
-        }
+
+    public String getId() {
+        return id;
     }
 
     /**
-     * For now the unique id for the track is its name.  This <may> not be unique,
-     * but we have nothing better.
-     *
-     * @return
+     * Ignored by default.  Subclases should override as required
      */
-    public String getId() {
-        return id;
+    public void preloadData(String chr, int start, int end, int zoom) {
     }
 
     /**
      * Method description
      *
-     * @param displayName
+     * @param name
      */
-    public void setDisplayName(String displayName) {
-        this.displayName = displayName;
+    public void setName(String name) {
+        this.name = name;
     }
 
     // TODO Provided for session saving.  Rename/refactor later
-    public String getActualDisplayName() {
-        return displayName;
-    }
+
 
     /**
      * Method description
      *
      * @return
      */
-    public String getDisplayName() {
+    public String getName() {
 
-        if (displayName != null) {
-            return displayName;
-        }
+        return name;
+    }
 
-        String sampleName = PreferenceManager.getInstance().getTrackAttributeName();
-        if (sampleName != null) {
-            sampleName = getAttributeValue(sampleName);
+    private String getDisplayName() {
+
+        String sampleKey = IGVMainFrame.getInstance().getSession().getTrackAttributeName();
+        if (sampleKey != null && sampleKey.trim().length() > 0) {
+            String name = getAttributeValue(sampleKey.trim());
+            if (name != null) {
+                return name;
+            }
+        }
+        return getName();
+    }
+    
+    
+    public String getSampleId() {
+        if(sampleId != null)  {
+            return sampleId;
+        }
+        else {
+            return getName();
         }
+    }
 
-        return ((sampleName == null) ? name : sampleName);
+    public void setSampleId(String sampleId) {
+        this.sampleId = sampleId;
     }
 
     /**
@@ -313,99 +282,66 @@ public abstract class AbstractTrack implements Track {
         }
     }
 
-    public void renderName(Graphics2D graphics, Rectangle trackRectangle, Rectangle visibleRect) {
-
-        Rectangle rect = null;
-        if (visibleRect != null) {
-            Rectangle intersectedRect = trackRectangle.intersection(visibleRect);
-            if (intersectedRect.height > 15) {
-                rect = intersectedRect;
-            } else {
-                rect = new Rectangle(trackRectangle);
-            }
-        }
+    public void renderName(Graphics2D graphics, Rectangle trackRectangle, Rectangle visibleRectangle) {
 
-        if (isSelected()) {
-            graphics.setBackground(Color.LIGHT_GRAY);
-        } else {
-            graphics.setBackground(Color.WHITE);
-        }
+        Rectangle rect = getDisplayableRect(trackRectangle, visibleRectangle);
 
         String trackName = getDisplayName();
-        if ((trackName != null) && (rect.getHeight() > 1)) {
-
-            Graphics2D g2D = (Graphics2D) graphics.create();
+        if ((trackName != null)) {
+            if (isSelected()) {
+                graphics.setBackground(Color.LIGHT_GRAY);
+            } else {
+                graphics.setBackground(Color.WHITE);
+            }
+            Graphics2D g2D = graphics; //(Graphics2D) graphics.create();
             g2D.clearRect(rect.x, rect.y, rect.width, rect.height);
+            if (rect.getHeight() > 3) {
 
-            // Calculate fontsize
-            int fontSize = g2D.getFont().getSize();
-            fontSize = (int) (fontSize * (rect.getHeight() / 20.0));
-            fontSize = (fontSize > 12) ? 12 : fontSize;
 
-            Font font = FontManager.getScalableFont(fontSize);
-            g2D.setFont(font);
-            FontManager.applyScalableTextHints(g2D);
+                // Calculate fontsize
+                int gap = Math.min(4, rect.height / 3);
+                int fontSize = Math.min(12, rect.height - gap);
 
-            FontMetrics fontMetrics = g2D.getFontMetrics();
+                Font font = FontManager.getScalableFont(fontSize);
+                g2D.setFont(font);
+                FontManager.applyScalableTextHints(g2D);
 
+                GraphicUtils.drawWrappedText(trackName, rect, g2D, false);
 
-            Rectangle2D stringBounds = fontMetrics.getStringBounds(trackName, g2D);
-            int textHeight = (int) stringBounds.getHeight() + 5;
-            double textWidth = stringBounds.getWidth() + 10;
-            if (textWidth < trackRectangle.width) {
-                GraphicUtils.drawVerticallyCenteredText(trackName, 5, rect, g2D, false);
+                //g2D.dispose();
             } else {
-                int charWidth = fontMetrics.charWidth('a');
-                int charsPerLine = (int) rect.width / charWidth;
-                int nStrings = (trackName.length() / charsPerLine) + 1;
-                if (nStrings * textHeight > rect.height) {
-                    GraphicUtils.drawVerticallyCenteredText(trackName, 5, rect, g2D, false);
-                } else {
-                    int breakPoint = 0;
-                    Rectangle tmp = new Rectangle(rect);
-                    tmp.y -= ((nStrings - 1) * textHeight) / 2;
-                    while (breakPoint < trackName.length()) {
-                        int end = Math.min(trackName.length(), breakPoint + charsPerLine);
-                        GraphicUtils.drawVerticallyCenteredText(trackName.substring(breakPoint, end), 5, tmp, g2D, false);
-                        breakPoint += charsPerLine;
-                        tmp.y += textHeight;
-                    }
+
+                /*Font font = FontManager.getScalableFont(2);
+                graphics.setFont(font);
+                FontMetrics fm = graphics.getFontMetrics();
+                int nameWidth = Math.min(rect.width - 2, fm.stringWidth(trackName));
+
+                graphics.setColor(Color.GRAY);
+                int y = (rect.y + rect.height / 2);
+                int start = rect.x + 1;
+                while (start < nameWidth) {
+                    graphics.drawLine(start, y, start + 1, y);
+                    start += 2 + (int) (2 * Math.random() * 4);
                 }
-            }
+                */
 
-            g2D.dispose();
+            }
         }
-
     }
 
-    final private StringBuffer buffer = new StringBuffer();
-
-    final private String addEllipseToString(String text, int panelWidth, FontMetrics fontMetrics,
-                                            Graphics2D gc) {
-
-        int textLength = text.length();
-        Rectangle2D stringBounds = fontMetrics.getStringBounds(text, gc);
-        double textWidth = stringBounds.getWidth() + 10;
-
-        // If text is wider than the panel - add ellipses
-        if ((textWidth > 0) && (textWidth > panelWidth)) {
-
-            // This is the percentage (of original text) to make new text
-            double reductionPercentage = panelWidth / (textWidth);
-
-            int expectedTextLength = (int) (textLength * reductionPercentage);
 
-            int leftEnd = (expectedTextLength / 2) - 2;    // Remove 2 more characters
-            int rightStart = (textLength - leftEnd);
-
-
-            buffer.delete(0, buffer.length());
-            buffer.append(text.substring(0, leftEnd));
-            buffer.append("...");
-            buffer.append(text.substring(rightStart, textLength));
-            return buffer.toString();
+    private Rectangle getDisplayableRect(Rectangle trackRectangle, Rectangle visibleRect) {
+        Rectangle rect = null;
+        if (visibleRect != null) {
+            Rectangle intersectedRect = trackRectangle.intersection(visibleRect);
+            if (intersectedRect.height > 15) {
+                rect = intersectedRect;
+            } else {
+                rect = new Rectangle(trackRectangle);
+            }
         }
-        return text;
+        return rect;
+
     }
 
     /**
@@ -418,11 +354,7 @@ public abstract class AbstractTrack implements Track {
     public void overlay(RenderContext context, Rectangle rect) {
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public Color getColor() {
         if (posColor != null) {
             return posColor;
@@ -442,18 +374,13 @@ public abstract class AbstractTrack implements Track {
                 return ColorUtilities.convertRGBStringToColor(rgb.replace("\"", ""));
 
             } catch (Exception exception) {
-                log.info("Invalid color string " + rgb + " for track: " + getId());
+                log.info("Invalid color string " + rgb + " for track: " + getName());
             }
         }
         return defaultValue;
     }
 
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public ResourceLocator getResourceLocator() {
         return resourceLocator;
     }
@@ -473,26 +400,16 @@ public abstract class AbstractTrack implements Track {
         AttributeManager.getInstance().addAttributeKey(uppercaseKey);
     }
 
-    /**
-     * Method description
-     *
-     * @param attributeKey
-     * @return
-     */
+
     public String getAttributeValue(String attributeKey) {
         String value = attributes.get(attributeKey);
         if (value == null) {
-            value = AttributeManager.getInstance().getAttribute(getId(), attributeKey);
+            value = AttributeManager.getInstance().getAttribute(getName(), attributeKey);
         }
         return value;
     }
 
-    /**
-     * Method description
-     *
-     * @param y
-     * @return
-     */
+
     public int getLevelNumber(int y) {
         return (preferredHeight == 0) ? 0 : y / preferredHeight;
     }
@@ -512,155 +429,86 @@ public abstract class AbstractTrack implements Track {
         }
     }
 
-    /**
-     * Method description
-     *
-     * @param minimumHeight
-     */
+
     public void setMinimumHeight(int minimumHeight) {
         this.minimumHeight = minimumHeight;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public int getMinimumHeight() {
         return minimumHeight;
     }
 
-    /**
-     * Method description
-     *
-     * @param type
-     */
+
     public void setTrackType(TrackType type) {
         this.trackType = type;
-        if (trackType != null) {
-            ContinuousColorScale cs = (ContinuousColorScale) PreferenceManager.getInstance().getColorScale(trackType);
-            if (cs != null) {
-                colorScale = cs;
-            }
-        }
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public TrackType getTrackType() {
         return trackType;
     }
 
     protected ViewContext getViewContext() {
-        return IGVModel.getInstance().getViewContext();
+        return ViewContext.getInstance();
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public boolean isVisible() {
-        return visible && ((getTrackType() != IGVConstants.overlayTrackType) || (this.overlayVisible == true));
+        return visible && ((getTrackType() != UIConstants.overlayTrackType) || (this.overlayVisible == true));
     }
 
     public void setColor(Color color) {
         this.posColor = color;
     }
 
-    /**
-     * Method description
-     *
-     * @param color
-     */
+
     public void setAltColor(Color color) {
         altColor = color;
     }
 
-    /**
-     * Method description
-     *
-     * @param isVisible
-     */
+
     public void setVisible(boolean isVisible) {
         this.visible = isVisible;
     }
 
-    /**
-     * Method description
-     *
-     * @param bool
-     */
+
     public void setOverlayVisible(boolean bool) {
         this.overlayVisible = bool;
     }
 
-    /**
-     * Method description
-     * <p/>
-     * <<<<<<< .working
-     * =======
-     */
+
     public void updateState() {
     }
 
-    /**
-     * Method description
-     * <p/>
-     * >>>>>>> .merge-right.r3291
-     *
-     * @param selected
-     */
+
     public void setSelected(boolean selected) {
         isSelected = selected;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public boolean isSelected() {
         return isSelected;
     }
 
-    /**
-     * Method description
-     *
-     * @param value
-     */
+
     public void setIsDraggable(boolean value) {
         isDraggable = value;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public boolean isDraggable() {
         return isDraggable;
     }
 
     int height = -1;
 
-    /**
-     * Method description
-     *
-     * @param preferredHeight
-     */
-    public void setHeight(int preferredHeight) {
-        this.height = preferredHeight;
+
+    public void setHeight(int height) {
+        this.height = height;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public int getHeight() {
         return (height < 0) ? getDefaultHeight() : height;
     }
@@ -669,17 +517,6 @@ public abstract class AbstractTrack implements Track {
         return getHeight();
     }
 
-    /**
-     * Method description
-     *
-     *
-     * @return
-     */
-    /**
-     * Method description
-     *
-     * @return
-     */
 
     public DataRange getDataRange() {
         if (dataRange == null) {
@@ -693,11 +530,7 @@ public abstract class AbstractTrack implements Track {
         return dataRange;
     }
 
-    /**
-     * Method description
-     *
-     * @param axisDefinition
-     */
+
     public void setDataRange(DataRange axisDefinition) {
         this.dataRange = axisDefinition;
     }
@@ -709,6 +542,7 @@ public abstract class AbstractTrack implements Track {
     }
 
     // Assumes features are sorted by start position
+
     protected LocusScore getFeatureAt(double position, double minWidth,
                                       List<? extends LocusScore> features) {
 
@@ -725,68 +559,35 @@ public abstract class AbstractTrack implements Track {
     public void refreshData(long timestamp) {
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public String getSourceFile() {
         return sourceFile;
     }
 
-    /**
-     * Method description
-     *
-     * @param sourceFile
-     */
     public void setSourceFile(String sourceFile) {
         this.sourceFile = sourceFile;
     }
 
     boolean expanded = false;
 
-    /**
-     * Method description
-     *
-     * @param expanded
-     */
     public void setExpanded(boolean expanded) {
         this.expanded = expanded;
     }
 
-    /**
-     * @return
-     */
     public boolean isExpanded() {
         return expanded;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public Collection<WindowFunction> getAvailableWindowFunctions() {
         return new ArrayList();
 
     }
 
-    Color midColor;
-
-    /**
-     * Method description
-     *
-     * @return
-     */
     public Color getMidColor() {
         return midColor;
     }
 
-    /**
-     * Method description
-     *
-     * @param midColor
-     */
     public void setMidColor(Color midColor) {
         this.midColor = midColor;
     }
@@ -796,13 +597,20 @@ public abstract class AbstractTrack implements Track {
         return false;
     }
 
-    /**
-     * Method description
-     *
-     * @param trackProperties
-     */
+
     public void setTrackProperties(TrackProperties trackProperties) {
 
+        this.itemRGB = trackProperties.isItemRGB();
+        this.useScore = trackProperties.isUseScore();
+        this.viewLimitMin = trackProperties.getMinValue();
+        this.viewLimitMax = trackProperties.getMaxValue();
+
+        // If view limits are explicitly set turn off autoscale
+        // TODO -- get rid of this ugly instance of and casting business
+        if (!Float.isNaN(viewLimitMin) && !Float.isNaN(viewLimitMax) && (this instanceof DataTrack)) {
+            ((DataTrack) this).setAutoscale(false);
+        }
+
         // Color scale properties
         if (!trackProperties.isAutoScale() &&
                 !Float.isNaN(trackProperties.getMinValue()) &&
@@ -820,9 +628,14 @@ public abstract class AbstractTrack implements Track {
                 }
             }
 
+
             DataRange dr = new DataRange(min, mid, max);
             setDataRange(dr);
 
+            if (trackProperties.isLogScale()) {
+                dr.setType(DataRange.Type.LOG);
+            }
+
             // If the user has explicity set a data range and colors apply to heatmap as well
             Color maxColor = trackProperties.getColor();
             Color minColor = trackProperties.getAltColor();
@@ -842,8 +655,10 @@ public abstract class AbstractTrack implements Track {
 
 
         }
+
+
         if (trackProperties.getName() != null) {
-            setDisplayName(trackProperties.getName());
+            name = trackProperties.getName();
         }
         if (trackProperties.getColor() != null) {
             setColor(trackProperties.getColor());
@@ -866,6 +681,10 @@ public abstract class AbstractTrack implements Track {
         if (trackProperties.getWindowingFunction() != null) {
             setStatType(trackProperties.getWindowingFunction());
         }
+        if (trackProperties.getUrl() != null) {
+            setUrl(trackProperties.getUrl());
+        }
+
     }
 
     /**
@@ -894,6 +713,12 @@ public abstract class AbstractTrack implements Track {
      */
     public ContinuousColorScale getColorScale() {
         if (colorScale == null) {
+
+            ContinuousColorScale defaultScale = IGVMainFrame.getInstance().getSession().getColorScale(trackType);
+            if (defaultScale != null) {
+                return defaultScale;
+            }
+
             double min = dataRange == null ? 0 : dataRange.getMinimum();
             double max = dataRange == null ? 10 : dataRange.getMaximum();
             Color c = getColor();
@@ -904,7 +729,7 @@ public abstract class AbstractTrack implements Track {
             } else {
                 colorScale = new ContinuousColorScale(min, max, minColor, c);
             }
-            colorScale.setNoDataColor(IGVConstants.NO_DATA_COLOR);
+            colorScale.setNoDataColor(UIConstants.NO_DATA_COLOR);
         }
         return colorScale;
     }
@@ -919,30 +744,38 @@ public abstract class AbstractTrack implements Track {
     }
 
 
-    public Map<String, String> getSessionAttributes() {
+    public Map<String, String> getPersistentState() {
 
         LinkedHashMap<String, String> attributes = new LinkedHashMap();
 
-        attributes.put(SessionManager.SessionAttribute.ID.getText(), getId());
-
+        // Color scale
         if (colorScale != null && !colorScale.isDefault()) {
-            attributes.put(SessionManager.SessionAttribute.COLOR_SCALE.getText(), colorScale.asString());
+            attributes.put(SessionReader.SessionAttribute.COLOR_SCALE.getText(), colorScale.asString());
         }
 
-        attributes.put(SessionManager.SessionAttribute.VISIBLE.getText(), String.valueOf(isVisible()));
+        attributes.put("showDataRange", String.valueOf(showDataRange));
+
+        // Visibility
+        attributes.put(SessionReader.SessionAttribute.VISIBLE.getText(), String.valueOf(isVisible()));
+
+        // expanded
+        attributes.put(SessionReader.SessionAttribute.EXPAND.getText(), String.valueOf(isExpanded()));
+
+        // height
         int height = getHeight();
-        if ((this instanceof FeatureTrack) && ((FeatureTrack) this).isExpanded()) {
+        if ((this instanceof FeatureTrack) && isExpanded()) {
             height = height / ((FeatureTrack) this).getNumberOfFeatureLevels();
         }
         String value = Integer.toString(height);
-        attributes.put(SessionManager.SessionAttribute.HEIGHT.getText(), value);
+        attributes.put(SessionReader.SessionAttribute.HEIGHT.getText(), value);
+
 
-        String displayName = getActualDisplayName();
-        if (displayName != null) {
-            attributes.put(SessionManager.SessionAttribute.DISPLAY_NAME.getText(), displayName);
+        if (name != null) {
+            attributes.put(SessionReader.SessionAttribute.NAME.getText(), name);
 
         }
 
+        // color
         Color color = getColor();
         if (color != null) {
             StringBuffer stringBuffer = new StringBuffer();
@@ -951,54 +784,51 @@ public abstract class AbstractTrack implements Track {
             stringBuffer.append(color.getGreen());
             stringBuffer.append(",");
             stringBuffer.append(color.getBlue());
-            attributes.put(SessionManager.SessionAttribute.COLOR.getText(), stringBuffer.toString());
+            attributes.put(SessionReader.SessionAttribute.COLOR.getText(), stringBuffer.toString());
         }
 
+        // renderer
         Renderer renderer = getRenderer();
         if (renderer != null) {
             RendererFactory.RendererType type = RendererFactory.getRenderType(renderer);
             if (type != null) {
-                attributes.put(SessionManager.SessionAttribute.RENDERER.getText(), type.name());
+                attributes.put(SessionReader.SessionAttribute.RENDERER.getText(), type.name());
             }
         }
 
+        // window function
         WindowFunction wf = getWindowFunction();
         if (wf != null) {
-            attributes.put(SessionManager.SessionAttribute.WINDOW_FUNCTION.getText(), wf.name());
+            attributes.put(SessionReader.SessionAttribute.WINDOW_FUNCTION.getText(), wf.name());
         }
 
-        DataRange dataRange = getDataRange();
-        if (dataRange != null) {
-            StringBuffer stringBuffer = new StringBuffer();
-            stringBuffer.append(dataRange.getMinimum());
-            stringBuffer.append(",");
-            stringBuffer.append(dataRange.getBaseline());
-            stringBuffer.append(",");
-            stringBuffer.append(dataRange.getMaximum());
-            attributes.put(SessionManager.SessionAttribute.SCALE.getText(), stringBuffer.toString());
-        }
 
         if (this instanceof FeatureTrack) {
             boolean expand = this.isExpanded();
-            attributes.put(SessionManager.SessionAttribute.EXPAND.getText(), String.valueOf(expand));
-
+            attributes.put(SessionReader.SessionAttribute.EXPAND.getText(), String.valueOf(expand));
         }
 
+        attributes.put("fontSize", String.valueOf(getFontSize()));
+
         return attributes;
     }
 
 
-    public void update(Map<String, String> attributes) {
+    public void restorePersistentState(Map<String, String> attributes) {
 
-        String displayName = attributes.get(SessionManager.SessionAttribute.DISPLAY_NAME.getText());
-        String isVisible = attributes.get(SessionManager.SessionAttribute.VISIBLE.getText());
-        String height = attributes.get(SessionManager.SessionAttribute.HEIGHT.getText());
-        String color = attributes.get(SessionManager.SessionAttribute.COLOR.getText());
-        String rendererType = attributes.get(SessionManager.SessionAttribute.RENDERER.getText());
-        String windowFunction = attributes.get(SessionManager.SessionAttribute.WINDOW_FUNCTION.getText());
-        String scale = attributes.get(SessionManager.SessionAttribute.SCALE.getText());
-        String isExpanded = attributes.get(SessionManager.SessionAttribute.EXPAND.getText());
-        String colorScale = attributes.get(SessionManager.SessionAttribute.COLOR_SCALE.getText());
+        String displayName = attributes.get(SessionReader.SessionAttribute.DISPLAY_NAME.getText());
+        String name = attributes.get(SessionReader.SessionAttribute.NAME.getText());
+
+        String isVisible = attributes.get(SessionReader.SessionAttribute.VISIBLE.getText());
+        String height = attributes.get(SessionReader.SessionAttribute.HEIGHT.getText());
+        String color = attributes.get(SessionReader.SessionAttribute.COLOR.getText());
+        String rendererType = attributes.get(SessionReader.SessionAttribute.RENDERER.getText());
+        String windowFunction = attributes.get(SessionReader.SessionAttribute.WINDOW_FUNCTION.getText());
+        String scale = attributes.get(SessionReader.SessionAttribute.SCALE.getText());
+        String isExpanded = attributes.get(SessionReader.SessionAttribute.EXPAND.getText());
+        String colorScale = attributes.get(SessionReader.SessionAttribute.COLOR_SCALE.getText());
+        String fontSizeString = attributes.get("fontSize");
+        String showDataRangeString = attributes.get("showDataRange");
 
         if (colorScale != null) {
             ColorScale cs = ColorScaleFactory.getScaleFromString(colorScale);
@@ -1008,8 +838,10 @@ public abstract class AbstractTrack implements Track {
             }
         }
 
-        if (displayName != null && displayName.length() > 0) {
-            setDisplayName(displayName);
+        if (name != null && name.length() > 0) {
+            setName(name);
+        } else if (displayName != null && displayName.length() > 0) {
+            setName(displayName);
         }
 
         // Set visibility
@@ -1021,9 +853,29 @@ public abstract class AbstractTrack implements Track {
             }
         }
 
+        if (showDataRangeString != null) {
+            try {
+                showDataRange = Boolean.parseBoolean(showDataRangeString);
+            } catch (Exception e) {
+                log.error("Error parsing data range: " + showDataRangeString);
+            }
+        }
+
         // Set height
         if (height != null) {
-            setHeight(Integer.parseInt(height));
+            try {
+                setHeight(Integer.parseInt(height));
+            } catch (NumberFormatException e) {
+                log.error("Error restoring track height: " + height);
+            }
+        }
+
+        if (fontSizeString != null) {
+            try {
+                setFontSize(Integer.parseInt(fontSizeString));
+            } catch (NumberFormatException e) {
+                log.error("Error restoring font size: " + fontSizeString);
+            }
         }
 
         // Set color
@@ -1065,4 +917,87 @@ public abstract class AbstractTrack implements Track {
     }
 
 
+    public boolean isItemRGB() {
+        return itemRGB;
+    }
+
+    public boolean isUseScore() {
+        return useScore;
+    }
+
+    public float getViewLimitMin() {
+        return viewLimitMin;
+    }
+
+    public float getViewLimitMax() {
+        return viewLimitMax;
+    }
+
+    public int getFontSize() {
+        return fontSize;
+    }
+
+    public void setFontSize(int fontSize) {
+        this.fontSize = fontSize;
+    }
+
+    public boolean isShowDataRange() {
+        return showDataRange;
+    }
+
+    public void setShowDataRange(boolean showDataRange) {
+        this.showDataRange = showDataRange;
+    }
+
+
+    /**
+     * Overriden by subclasses
+     *
+     * @param e
+     * @return
+     */
+    public Feature getFeatureAtMousePosition(MouseEvent e) {
+        return null;
+    }
+
+    public String getId_142() {
+        return id_142 == null ? getName() : id_142;
+    }
+
+    public void setId_142(String id_142) {
+        this.id_142 = id_142;
+    }
+
+    /**
+     * Special normalization function for linear (non logged) copy number data
+     *
+     * @param value
+     * @param norm
+     * @return
+     */
+    public static float getLogNormalizedValue(float value, double norm) {
+        if (norm == 0) {
+            return Float.NaN;
+        } else {
+            return (float) (Math.log(Math.max(Float.MIN_VALUE, value) / norm) / DataSourceTrack.log2);
+        }
+    }
+
+    public  float logScaleData(float dataY) {
+
+        // Special case for copy # -- centers data around 2 copies (1 for allele
+        // specific) and log normalizes
+        if (((getTrackType() == TrackType.COPY_NUMBER) ||
+                (getTrackType() == TrackType.ALLELE_SPECIFIC_COPY_NUMBER) ||
+                (getTrackType() == TrackType.CNV)) &&
+                !isLogNormalized()) {
+            double centerValue = (getTrackType() == TrackType.ALLELE_SPECIFIC_COPY_NUMBER)
+                    ? 1.0 : 2.0;
+
+            dataY = getLogNormalizedValue(dataY, centerValue);
+        }
+
+
+        return dataY;
+    }
 }
diff --git a/src/org/broad/igv/track/Attribute.java b/src/org/broad/igv/track/Attribute.java
deleted file mode 100644
index cda3751..0000000
--- a/src/org/broad/igv/track/Attribute.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-package org.broad.igv.track;
-
-/**
- * Represents a specific attribute instance.
- *
- * @author jrobinso
- */
-public class Attribute {
-    private String trackId;
-    private String key;
-    private String value;
-
-    public Attribute(String trackId, String key, String value) {
-        this.trackId = trackId;
-        this.key = key.toUpperCase();
-        this.value = value;
-    }
-
-    public String getTrackId() {
-        return trackId;
-    }
-
-    public String getKey() {
-        return key;
-    }
-
-    public String getValue() {
-        return value;
-    }
-
-}
diff --git a/src/org/broad/igv/track/AttributeManager.java b/src/org/broad/igv/track/AttributeManager.java
index b473ad2..d3bbe08 100644
--- a/src/org/broad/igv/track/AttributeManager.java
+++ b/src/org/broad/igv/track/AttributeManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -24,10 +24,11 @@
 package org.broad.igv.track;
 
 import org.apache.log4j.Logger;
+import org.broad.igv.exceptions.DataLoadException;
 import org.broad.igv.exceptions.ParserException;
-import org.broad.igv.util.ParsingUtils;
 import org.broad.igv.ui.IGVMainFrame;
 import org.broad.igv.util.AsciiLineReader;
+import org.broad.igv.util.ParsingUtils;
 import org.broad.igv.util.ResourceLocator;
 import org.broad.igv.util.Utilities;
 
@@ -35,6 +36,7 @@ import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.beans.PropertyChangeSupport;
 import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.util.*;
 
 /**
@@ -43,35 +45,44 @@ import java.util.*;
 public class AttributeManager {
 
     private static Logger log = Logger.getLogger(AttributeManager.class);
+
+    private static AttributeManager singleton;
+    final public static String ATTRIBUTES_LOADED_PROPERTY = "ATTRIBUTES_LOADED_PROPERTY";
+    final public static String ATTRIBUTES_NARROWED_PROPERTY = "ATTRIBUTES_NARROWED_PROPERTY";
+
+    private PropertyChangeSupport propertyChangeSupport;
+
     /**
      * The set of currently loaded attribute resource files
      */
     Set<ResourceLocator> loadedResources = new HashSet();
-    // Stores current file selections as String
-    //private String currentAttributeFiles = null;
-    final public static String ATTRIBUTES_LOADED_PROPERTY =
-            "ATTRIBUTES_LOADED_PROPERTY";
-    final public static String ATTRIBUTES_NARROWED_PROPERTY =
-            "ATTRIBUTES_NARROWED_PROPERTY";
-    // Property Change Support
-    private PropertyChangeSupport propertyChangeSupport;
+
+
     /**
      * Map of data track identifiers (i.e. "array names") to its
      * attributeMap.   The attributeMap for a track maps attribute name (for
      * example "Cell Type"  to value (for example "ES");
      */
     LinkedHashMap<String, Map<String, String>> attributeMap = new LinkedHashMap();
+
     /**
      * List of attribute names (keys).  The list
      * is kept so the keys may be fetched in the order they were added.
      */
     List<String> attributeKeys = new ArrayList();
+
+
+    /**
+     * Attributes to hide, usually selected by the user
+     */
+    Set<String> hiddenAttributes = new HashSet();
+
     /**
      * The complete set of unique attribute values per attribute key.  This is useful in
      * assigning unique colors
      */
     Map<String, Set<String>> uniqueAttributeValues;
-    static private AttributeManager singleton;
+
 
     private AttributeManager() {
         propertyChangeSupport = new PropertyChangeSupport(this);
@@ -96,55 +107,54 @@ public class AttributeManager {
     }
 
     /**
-     * Return the attribute value for the given track (trackIdentifier) and key.
+     * Return the attribute value for the given track (trackName) and key.
      * In general this method should not be used,  use
      * Track.getAttributeValue(key)
      * instead.
      */
-    protected String getAttribute(String trackIdentifier, String attributeKey) {
-        Map attributes = attributeMap.get(trackIdentifier);
+    protected String getAttribute(String trackName, String attributeKey) {
+        Map attributes = attributeMap.get(trackName);
         return (attributes == null ? null : (String) attributes.get(attributeKey));
     }
 
     /**
-     * Return all attributes for a given track as a map
-     *
-     * @param trackIdentifier
-     * @return
+     * Return the list of attribute names (keys) in the order they should
+     * be displayed.
      */
-    protected Map<String, String> getAllAttributes(String trackIdentifier) {
-        return attributeMap.get(trackIdentifier);
+    public List<String> getAttributeKeys() {
+        return new ArrayList(attributeKeys);
     }
 
-    public void addAttributes(LinkedHashSet<Attribute> attributes) {
 
-        try {
-            for (Attribute attr : attributes) {
-                setAttribute(attr.getTrackId(), attr.getKey(), attr.getValue());
-            }
-        } finally {
-
-            IGVMainFrame.getInstance().packViews();
-
-            // Repaint
-            IGVMainFrame.getInstance().repaintDataAndHeaderPanels();
-            IGVMainFrame.getInstance().repaintStatusAndZoomSlider();
+    public Set<String> getHiddenAttributes() {
+        return hiddenAttributes;
+    }
 
-            // Notify Listeners
-            firePropertyChange(this, ATTRIBUTES_LOADED_PROPERTY, null, null);
+    public void setHiddenAttributes(Set<String> attributes) {
+        // Interpret null as the empty set
+        if (attributes == null) {
+            hiddenAttributes.clear();
+        } else {
+            hiddenAttributes = attributes;
         }
     }
 
-    public void addAttributeKey(String key) {
-        if (!attributeKeys.contains(key) && !key.startsWith("#")) {
-            attributeKeys.add(key);
-        }
+    public void clearAllAttributes() {
+        attributeMap.clear();
+        attributeKeys.clear();
+        uniqueAttributeValues.clear();
+        hiddenAttributes.clear();
+        loadedResources = new HashSet();
     }
 
+
+    //////////////////////////
+
+
     /**
      * Set the attribute value for the given track and key.
      */
-    private void setAttribute(String trackIdentifier, String attributeKey, String attributeValue) {
+    private void addAttribute(String trackIdentifier, String attributeKey, String attributeValue) {
 
         addAttributeKey(attributeKey);
 
@@ -166,95 +176,37 @@ public class AttributeManager {
         attributes.put(attributeKey, attributeValue);
     }
 
-    public void clearAllAttributes() {
-        attributeMap.clear();
-        attributeKeys.clear();
-        uniqueAttributeValues.clear();
-        loadedResources = new HashSet();
-    }
-
-    /**
-     * Find track identifiers (arrayNames) that match the given attribute key/value
-     * combination
-     */
-    public Set<String> findTracksMatching(String key, String value) {
-        HashSet<String> matchingTracks = new HashSet();
-        for (Map.Entry<String, Map<String, String>> entry : attributeMap.entrySet()) {
-            String trackAttributeValue = entry.getValue().get(key);
-            if (trackAttributeValue != null && trackAttributeValue.equals(value)) {
-                matchingTracks.add(entry.getKey());
-            }
+    public void addAttributeKey(String key) {
+        if (!attributeKeys.contains(key) && !key.startsWith("#")) {
+            attributeKeys.add(key);
         }
-        return matchingTracks;
     }
 
-    /**
-     * Return the list of attribute names (keys) in the order they should
-     * be displayed.
-     */
-    public List<String> getAttributeKeys() {
-        return attributeKeys;
-    }
 
     /**
-     * Load attributes from an ascii file in "SampleInfo" format.
+     * Load attributes from an ascii file in "Sample Info" format.
      */
-    public HashSet loadAttributes(ResourceLocator locator) {
+    public void loadSampleInfo(ResourceLocator locator) {
         AsciiLineReader reader = null;
         String nextLine = null;
-        LinkedHashSet<Attribute> attributeList = null;
         try {
-            attributeList = new LinkedHashSet();
             reader = ParsingUtils.openAsciiReader(locator);
             nextLine = reader.readLine();
-
-            // Parse column neadings for attribute names.
-            // Columns 1 and 2 are array and sample name (not attributes)
-            String[] colHeadings = nextLine.split("\t");
-
-            while ((nextLine = reader.readLine()) != null) {
-
-                String[] values = nextLine.split("\t");
-
-                if (values.length >= 2) {
-                    String arrayName = values[0].trim();
-                    // Loop through attribute columns
-                    for (int i = 1; i < Math.min(values.length, colHeadings.length); i++) {
-                        String attributeName = colHeadings[i].trim();
-                        String attributeValue = values[i].trim();
-                        if (attributeValue.length() > 0) {
-                            attributeList.add(new Attribute(arrayName,
-                                    attributeName, attributeValue));
-                        }
-                    }
-                }
-            }
-
-            reader.close();
-
-            if (attributeList.isEmpty()) {
-                throw new RuntimeException("File does not contain valid attribute data.");
+            if (nextLine.toLowerCase().startsWith("#sampletable")) {
+                loadSampleTable(reader, nextLine, locator.getPath());
+            } else {
+                loadOldSamapleInfo(reader, nextLine, locator.getPath());
             }
-
-            addAttributes(attributeList);
-
             loadedResources.add(locator);
 
             //createCurrentAttributeFileString(files);
-            TrackManager.getInstance().resetOverlayTracks();
+            IGVMainFrame.getInstance().getTrackManager().resetOverlayTracks();
+
+            IGVMainFrame.getInstance().doRefresh();
 
-            // If we get here everything went o.k.  This is signaled with a null
-            // return value (this convention is a bit odd).
-        } catch (FileNotFoundException ex) {
+        } catch (IOException ex) {
             log.error("Error loading attribute file", ex);
-            throw new RuntimeException("Attribute file not found: " + locator.getPath());
-        } catch (Exception ex) {
-            log.error(ex);
-            if (nextLine != null && reader.getCurrentLineNumber() != 0) {
-                throw new ParserException(ex.getMessage(), ex, reader.getCurrentLineNumber(), nextLine);
-            } else {
-                throw new RuntimeException(ex);
-            }
+            throw new DataLoadException("Error reading attribute file", locator.getPath());
         } finally {
             if (reader != null) {
                 reader.close();
@@ -262,10 +214,113 @@ public class AttributeManager {
             }
             firePropertyChange(this, ATTRIBUTES_LOADED_PROPERTY, null, null);
         }
+    }
+
+    private String loadOldSamapleInfo(AsciiLineReader reader, String nextLine, String path) throws IOException {
+        // Parse column neadings for attribute names.
+        // Columns 1 and 2 are array and sample name (not attributes)
+        boolean foundAttributes = false;
+        String[] colHeadings = nextLine.split("\t");
+        int nLines = 0;
+        int lineLimit = 100000;
+        while ((nextLine = reader.readLine()) != null) {
+            if (nLines++ > lineLimit) {
+                break;
+            }
+            String[] values = nextLine.split("\t");
+
+            if (values.length >= 2) {
+                String arrayName = values[0].trim();
+                // Loop through attribute columns
+                for (int i = 0; i < colHeadings.length; i++) {
+                    String attributeName = colHeadings[i].trim();
+                    String attributeValue = (i < values.length ? values[i].trim() : "");
+                    addAttribute(arrayName, attributeName, attributeValue);
+                    foundAttributes = true;
+                }
+            }
+        }
+
+
+        if (!foundAttributes) {
+            throw new DataLoadException("Could not determine file type.  Does file have proper extension? ", path);
+        }
+        return nextLine;
+    }
+
+    /**
+     * Load attributes from an ascii file in "Sample Table" format.  This format expects
+     * a sample table section, prefaced by #sampleTable,  followed by a sample mapping
+     * section  (track -> sample) prefaced by #sampleMappings
+     */
+    private void loadSampleTable(AsciiLineReader reader, String nextLine, String path) throws IOException {
+
+        // Parse column neadings for attribute names.
+        // Columns 1 and 2 are array and sample name (not attributes)
+        nextLine = reader.readLine();
+        String[] colHeadings = nextLine.split("\t");
+
+        // Map of sample -> attribute list
+        Map<String, List<Attribute>> sampleTable = new HashMap();
+
+        boolean foundAttributes = false;
+        int nLines = 0;
+        int lineLimit = 100000;
+        while ((nextLine = reader.readLine()) != null) {
+            if (nLines++ > lineLimit || nextLine.toLowerCase().startsWith("#samplemapping")) {
+                break;
+            }
+            String[] values = nextLine.split("\t");
+
+            if (values.length >= 2) {
+                String sampleName = values[0].trim();
+                // Loop through attribute columns
+                List<Attribute> attributes = new ArrayList(colHeadings.length);
+                for (int i = 0; i < colHeadings.length; i++) {
+                    String attributeName = colHeadings[i].trim();
+                    String attributeValue = (i < values.length ? values[i].trim() : "");
+                    attributes.add(new Attribute(attributeName, attributeValue));
+                    foundAttributes = true;
+                }
+                sampleTable.put(sampleName, attributes);
+            }
+        }
+        if (!foundAttributes) {
+            throw new DataLoadException("Could not determine file type.  Does file have proper extension? ", path);
+        }
+
+
+        if (nextLine.toLowerCase().startsWith("#samplemapping")) {
+            while ((nextLine = reader.readLine()) != null) {
+                String[] tokens = nextLine.split("\t");
+                if (tokens.length < 2) {
+                    continue;
+                }
+                String array = tokens[0];
+                String sample = tokens[1];
+                List<Attribute> attributes = sampleTable.get(sample);
+                if (attributes == null) {
+                    log.info("Warning: sample in mapping section:  " + sample + " in sample table file " + path);
+                } else {
+                    for (Attribute att : attributes) {
+                        addAttribute(array, att.getKey(), att.getValue());
+                    }
+                }
+            }
+        } else {
+            // No mapping section.
+            for (Map.Entry<String, List<Attribute>> entry : sampleTable.entrySet()) {
+                String sample = entry.getKey();
+                for (Attribute att : entry.getValue()) {
+                    addAttribute(sample, att.getKey(), att.getValue());
+                }
+            }
+        }
+
 
-        return attributeList;
     }
 
+
     public void firePropertyChange(Object source, String propertyName,
                                    Object oldValue, Object newValue) {
 
@@ -288,4 +343,28 @@ public class AttributeManager {
     public Set<ResourceLocator> getLoadedResources() {
         return loadedResources;
     }
+
+    /**
+     * Represents a specific attribute instance.
+     *
+     * @author jrobinso
+     */
+    private static class Attribute {
+        private String key;
+        private String value;
+
+        public Attribute(String key, String value) {
+            this.key = key.toUpperCase();
+            this.value = value;
+        }
+
+        public String getKey() {
+            return key;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+    }
 }
diff --git a/src/org/broad/igv/track/DataSourceTrack.java b/src/org/broad/igv/track/DataSourceTrack.java
index 845d2f1..0533cd3 100644
--- a/src/org/broad/igv/track/DataSourceTrack.java
+++ b/src/org/broad/igv/track/DataSourceTrack.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -21,7 +21,6 @@ package org.broad.igv.track;
 
 //~--- non-JDK imports --------------------------------------------------------
 
-import org.broad.igv.IGVConstants;
 import org.broad.igv.data.DataSource;
 import org.broad.igv.feature.LocusScore;
 import org.broad.igv.h5.ObjectNotFoundException;
@@ -39,6 +38,7 @@ import java.util.List;
 public class DataSourceTrack extends DataTrack {
 
     private DataSource dataSource;
+    public static double log2 = Math.log(2);
 
     // private WindowFunction windowFunction = WindowFunction.median;
 
@@ -49,14 +49,14 @@ public class DataSourceTrack extends DataTrack {
      * @param name
      * @param dataSource
      */
-    public DataSourceTrack(ResourceLocator locator, String name, DataSource dataSource) {
-        super(locator, name);
+    public DataSourceTrack(ResourceLocator locator, String id, String name, DataSource dataSource) {
+        super(locator, id, name);
 
         this.dataSource = dataSource;
         setTrackType(dataSource.getTrackType());
 
-        float min = (float) dataSource.getDataMin(IGVConstants.CHR_ALL);
-        float max = (float) dataSource.getDataMax(IGVConstants.CHR_ALL);
+        float min = (float) dataSource.getDataMin();
+        float max = (float) dataSource.getDataMax();
         float baseline = 0;
         if (min > 0) {
             min = 0;
@@ -128,23 +128,41 @@ public class DataSourceTrack extends DataTrack {
         }
         if (isRegionScoreType(type)) {
             List<LocusScore> scores = getSummaryScores(chr, start, end, zoom);
-            float regionScore = 0;
-            int intervalSum = 0;
-            for (LocusScore score : scores) {
-                if ((score.getEnd() >= start) && (score.getStart() <= end)) {
-                    int interval = Math.min(end, score.getEnd())
-                            - Math.max(start, score.getStart());
-                    float value = score.getScore();
-                    regionScore += value * interval;
-                    intervalSum += interval;
+            if (type == RegionScoreType.FLUX) {
+                float sumDiffs = 0;
+                float lastScore = Float.NaN;
+                for (LocusScore score : scores) {
+                    if ((score.getEnd() >= start) && (score.getStart() <= end)) {
+                        if (Float.isNaN(lastScore)) {
+                            lastScore = Math.min(2, Math.max(-2, logScaleData(score.getScore())));
+                        } else {
+                            float s = Math.min(2, Math.max(-2, logScaleData(score.getScore())));
+                            sumDiffs += Math.abs(s - lastScore);
+                            lastScore = s;
+                        }
+                    }
                 }
-            }
-            if (intervalSum <= 0) {
-                return -Float.MAX_VALUE;
+                return sumDiffs;
+
             } else {
-                regionScore /= intervalSum;
-                return (type == RegionScoreType.DELETION) ? -regionScore : regionScore;
+                float regionScore = 0;
+                int intervalSum = 0;
+                for (LocusScore score : scores) {
+                    if ((score.getEnd() >= start) && (score.getStart() <= end)) {
+                        int interval = Math.min(end, score.getEnd()) - Math.max(start, score.getStart());
+                        float value = score.getScore();
+                        regionScore += value * interval;
+                        intervalSum += interval;
+                    }
+                }
+                if (intervalSum <= 0) {
+                    return -Float.MAX_VALUE;
+                } else {
+                    regionScore /= intervalSum;
+                    return (type == RegionScoreType.DELETION) ? -regionScore : regionScore;
+                }
             }
+
         } else {
             return -Float.MAX_VALUE;
         }
diff --git a/src/org/broad/igv/track/DataTrack.java b/src/org/broad/igv/track/DataTrack.java
index 3a627b0..e0077d9 100644
--- a/src/org/broad/igv/track/DataTrack.java
+++ b/src/org/broad/igv/track/DataTrack.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -25,20 +25,22 @@
  */
 package org.broad.igv.track;
 
-//~--- non-JDK imports --------------------------------------------------------
-
 import org.apache.log4j.Logger;
+import org.broad.igv.PreferenceManager;
 import org.broad.igv.feature.FeatureUtils;
 import org.broad.igv.feature.LocusScore;
+import org.broad.igv.renderer.DataRange;
 import org.broad.igv.renderer.DataRenderer;
-import org.broad.igv.ui.IGVModel;
+import org.broad.igv.renderer.XYPlotRenderer;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.util.ResourceLocator;
 
 import java.awt.*;
 import java.util.List;
+import java.util.Map;
 
 /**
- * Represents a feature track backed by an HDF-5 file.
+ * Represents a track of numeric data
  *
  * @author jrobinso
  */
@@ -46,41 +48,59 @@ public abstract class DataTrack extends AbstractTrack {
 
     private static Logger log = Logger.getLogger(DataTrack.class);
     private DataRenderer renderer;
+    private boolean autoscale;
+
 
-    /**
-     * Constructs ...
-     *
-     * @param locator
-     * @param name
-     */
-    public DataTrack(ResourceLocator locator, String name) {
-        super(locator, name);
+    public DataTrack(ResourceLocator locator, String id, String name) {
+        super(locator, id, name);
+        autoscale = PreferenceManager.getInstance().getChartPreferences().isAutoscale();
+    }
+
+    public boolean isAutoscale() {
+        return autoscale;
+    }
 
+    public void setAutoscale(boolean autoscale) {
+        this.autoscale = autoscale;
     }
 
     public void preloadData(String chrName, int start, int end, int zoom) {
         getSummaryScores(chrName, start, end, zoom);
     }
 
-    /**
-     * Method description
-     *
-     * @param context
-     * @param rect
-     */
     public void render(RenderContext context, Rectangle rect) {
-        List<LocusScore> scores = getSummaryScores(context.getChr(),
-                (int) context.getStartLocation(),
+
+        List<LocusScore> scores = getSummaryScores(context.getChr(), (int) context.getOrigin(),
                 (int) context.getEndLocation() + 1, context.getZoom());
 
-        getRenderer().render(this, scores, context, rect);
+        List<LocusScore> inViewScores = scores;
+        if (autoscale) {
+            double origin = context.getOrigin();
+            double end = origin + context.getScale() * rect.width;
+            InViewInterval inter = computeScale(origin, end, scores);
+            if (inter.endIdx > inter.startIdx) {
+                inViewScores = scores.subList(inter.startIdx, inter.endIdx);
+
+                DataRange dr = getDataRange();
+                float min = Math.min(0, inter.dataMin);
+                float base = Math.max(min, dr.getBaseline());
+                float max = inter.dataMax;
+                // Pathological case where min ~= max  (no data in view)
+                if (max - min <= (2 * Float.MIN_VALUE)) {
+                    max = min + 1;
+                }
+
+                DataRange newDR = new DataRange(min, base, max, dr.isDrawBaseline());
+                newDR.setType(dr.getType());
+                setDataRange(newDR);
+            }
+
+        }
+
+        getRenderer().render(this, inViewScores, context, rect);
     }
 
-    /**
-     * Method description
-     *
-     * @param rc
-     */
+
     public void setRendererClass(Class rc) {
         try {
             renderer = (DataRenderer) rc.newInstance();
@@ -89,11 +109,7 @@ public abstract class DataTrack extends AbstractTrack {
         }
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public DataRenderer getRenderer() {
         if (renderer == null) {
             setRendererClass(getDefaultRendererClass());
@@ -101,32 +117,26 @@ public abstract class DataTrack extends AbstractTrack {
         return renderer;
     }
 
-    /**
-     * Method description
-     *
-     * @param chr
-     * @param position
-     * @param y
-     * @return
-     */
+
     public String getValueStringAt(String chr, double position, int y) {
+        StringBuffer buf = new StringBuffer();
+        buf.append(getName() + "<br>");
+        if ((getDataRange() != null) && (getRenderer() instanceof XYPlotRenderer)) {
+            buf.append("Data scale: " + getDataRange().getMinimum() + " - " + getDataRange().getMaximum() + "<br>");
+        }
+
         LocusScore score = getLocusScoreAt(chr, position);
-        return ((score == null) ? "" : score.getValueString(position, getWindowFunction()));
+        buf.append((score == null) ? "" : score.getValueString(position, getWindowFunction()));
+        return buf.toString();
     }
 
-    /**
-     * Method description
-     *
-     * @param chr
-     * @param position
-     * @return
-     */
+
     public LocusScore getLocusScoreAt(String chr, double position) {
-        int zoom = Math.max(0, IGVModel.getInstance().getViewContext().getZoom());
+        int zoom = Math.max(0, ViewContext.getInstance().getZoom());
         List<LocusScore> scores = getSummaryScores(chr, (int) position - 10, (int) position + 10, zoom);
 
         // give a 2 pixel window, otherwise very narrow features will be missed.
-        double bpPerPixel = IGVModel.getInstance().getViewContext().getScale();
+        double bpPerPixel = ViewContext.getInstance().getScale();
         double minWidth = 2 * bpPerPixel;    /* * */
 
         if (scores == null) {
@@ -136,70 +146,107 @@ public abstract class DataTrack extends AbstractTrack {
         }
     }
 
-    /**
-     * Method description
-     *
-     * @param chr
-     * @param startLocation
-     * @param endLocation
-     * @param zoom
-     * @return
-     */
+
     abstract public List<LocusScore> getSummaryScores(String chr, int startLocation,
                                                       int endLocation, int zoom);
 
-    /**
-     * Method description
-     *
-     * @param x
-     * @param y
-     * @return
-     */
     public boolean handleClick(int x, int y) {
 
         // Ignore
         return false;
     }
 
-    /**
-     * Method description
-     *
-     * @param color
-     */
     @Override
     public void setColor(Color color) {
         super.setColor(color);
     }
 
-    /**
-     * Method description
-     *
-     * @param color
-     */
+
     @Override
     public void setAltColor(Color color) {
         super.setAltColor(color);
 
     }
 
-    /**
-     * Method description
-     *
-     * @param color
-     */
+
     @Override
     public void setMidColor(Color color) {
         super.setMidColor(color);
 
     }
 
-    /**
-     * Method description
-     *
-     * @param type
-     * @return
-     */
     public boolean isRegionScoreType(RegionScoreType type) {
-        return ((getTrackType() == TrackType.GENE_EXPRESSION) && (type == RegionScoreType.EXPRESSION)) || (((getTrackType() == TrackType.COPY_NUMBER) || (getTrackType() == TrackType.ALLELE_SPECIFIC_COPY_NUMBER)) && ((type == RegionScoreType.AMPLIFICATION) || (type == RegionScoreType.DELETION))) || (type == RegionScoreType.SCORE);
+        return (getTrackType() == TrackType.GENE_EXPRESSION && type == RegionScoreType.EXPRESSION) ||
+                ((getTrackType() == TrackType.COPY_NUMBER || getTrackType() == TrackType.CNV ||
+                        getTrackType() == TrackType.ALLELE_SPECIFIC_COPY_NUMBER) &&
+                        (type == RegionScoreType.AMPLIFICATION ||
+                                type == RegionScoreType.DELETION ||
+                                type == RegionScoreType.FLUX)) ||
+                (type == RegionScoreType.SCORE);
+    }
+
+
+    private InViewInterval computeScale(double origin, double end, List<LocusScore> scores) {
+
+        InViewInterval interval = new InViewInterval();
+
+        if (scores.size() == 1) {
+            interval.dataMax = Math.max(0, scores.get(0).getScore());
+            interval.dataMin = Math.min(0, scores.get(0).getScore());
+        } else {
+            interval.startIdx = 0;
+            interval.endIdx = scores.size();
+            for (int i = 1; i < scores.size(); i++) {
+                if (scores.get(i).getEnd() >= origin) {
+                    interval.startIdx = i - 1;
+                    break;
+                }
+            }
+
+            for (int i = interval.startIdx + 1; i < scores.size(); i++) {
+                LocusScore locusScore = scores.get(i);
+                interval.dataMax = Math.max(interval.dataMax, locusScore.getScore());
+                interval.dataMin = Math.min(interval.dataMin, locusScore.getScore());
+                if (locusScore.getStart() > end) {
+                    interval.endIdx = i;
+                    break;
+                }
+            }
+        }
+
+        return interval;
+    }
+
+    @Override
+    public Map<String, String> getPersistentState() {
+        Map<String, String> properties = super.getPersistentState();
+        properties.put("autoscale", String.valueOf(autoscale));
+        return properties;
     }
+
+
+    @Override
+    public void restorePersistentState(Map<String, String> attributes) {
+        super.restorePersistentState(attributes);
+        String as = attributes.get("autoscale");
+        if (as != null) {
+            try {
+                autoscale = Boolean.parseBoolean(as);
+
+            }
+            catch (Exception e) {
+                log.error("Error restoring session.  Invalid autoscale value: " + autoscale);
+
+            }
+        }
+    }
+
+
+    class InViewInterval {
+        int startIdx;
+        int endIdx;
+        float dataMax = 0;
+        float dataMin = 0;
+    }
+
 }
diff --git a/src/org/broad/igv/track/FeatureCollectionSource.java b/src/org/broad/igv/track/FeatureCollectionSource.java
new file mode 100644
index 0000000..adbc0c6
--- /dev/null
+++ b/src/org/broad/igv/track/FeatureCollectionSource.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track;
+
+import org.broad.igv.Globals;
+import org.broad.igv.data.AbstractDataSource;
+import org.broad.igv.data.DataSource;
+import org.broad.igv.data.DataTile;
+import org.broad.igv.feature.*;
+import org.broad.igv.session.ViewContext;
+
+import java.util.*;
+
+/**
+ * Implementation of FeatureSource that wraps a list or map of features for the
+ * entire genome.  Instances are typically created by parsing a bed or gff file.  This
+ * is a legacy implementation, and does not scale to large feature tracks.
+ * <p/>
+ * User: jrobinso
+ * Date: Jan 31, 2010
+ */
+public class FeatureCollectionSource implements FeatureSource {
+
+    private TrackType type;
+
+    private TrackProperties properties;
+
+    private Map<String, List<Feature>> featureMap;
+
+    CoverageDataSource coverageData;
+
+    public FeatureCollectionSource(List<Feature> allFeatures) {
+        initFeatures(allFeatures);
+        coverageData = new CoverageDataSource();
+        coverageData.computeGenomeCoverage();
+        sampleGenomeFeatures();
+    }
+
+    /**
+     * NOTE:  This constructor assumes that feature lists are sorted.
+     *
+     * @param features
+     */
+    public FeatureCollectionSource(Map<String, List<Feature>> features) {
+        this.featureMap = features;
+        coverageData = new CoverageDataSource();
+        coverageData.computeGenomeCoverage();
+        sampleGenomeFeatures();
+
+    }
+
+    public List<LocusScore> getCoverageScores(String chr, int startLocation, int endLocation, int zoom) {
+        return coverageData == null ? Collections.<LocusScore>emptyList() :
+                coverageData.getSummaryScoresForRange(chr, startLocation, endLocation, zoom);
+    }
+
+    public DataSource getCoverageSource() {
+        return coverageData;
+    }
+
+    public Iterator<Feature> getFeatures(String chr, int start, int end) {
+        return featureMap.containsKey(chr) ? featureMap.get(chr).iterator() : null;
+    }
+
+    public List<Feature> getFeatures(String chr) {
+        return featureMap.get(chr);
+    }
+
+    public int getBinSize() {
+        return 0;
+    }
+
+    public void setBinSize(int size) {
+        // ignored
+    }
+
+
+    private void initFeatures(List<Feature> allFeatures) {
+        // Separate features by chromosome
+
+        featureMap = new HashMap();
+        for (Feature f : allFeatures) {
+            List<Feature> fList = featureMap.get(f.getChr());
+            if (fList == null) {
+                fList = new ArrayList();
+                featureMap.put(f.getChr(), fList);
+            }
+            fList.add(f);
+        }
+
+        for (List<Feature> featureList : featureMap.values()) {
+            FeatureUtils.sortFeatureList(featureList);
+        }
+
+        if (featureMap.size() < 100) {
+            sampleGenomeFeatures();
+        }
+    }
+
+
+    private void setFeatures(String chr, List<Feature> features) {
+        FeatureUtils.sortFeatureList(features);
+        featureMap.put(chr, features);
+    }
+
+    public TrackType getType() {
+        return type;
+    }
+
+    public void setType(TrackType type) {
+        this.type = type;
+    }
+
+    protected void sampleGenomeFeatures() {
+        List<Feature> chrAllFeatures = new ArrayList(1000);
+        Genome currentGenome = ViewContext.getInstance().getGenome();
+        int sampleLength = (int) ((double) currentGenome.getLength() / (1000 * 700));
+        int lastFeaturePosition = -1;
+        for (String chr : currentGenome.getChromosomeNames()) {
+            List<Feature> features = getFeatures(chr);
+            if (features != null) {
+                long offset = currentGenome.getCumulativeOffset(chr);
+                for (Feature f : features) {
+                    int genStart = (int) ((offset + f.getStart()) / 1000);
+                    int genEnd = (int) ((offset + f.getEnd()) / 1000);
+                    if (genEnd > lastFeaturePosition + sampleLength) {
+                        Feature f2 = (Feature) f.copy();
+                        //BasicFeature f2 = new BasicFeature(UIStringConstants.CHR_ALL, genStart, genEnd, f.getStrand());
+                        f2.setChr(Globals.CHR_ALL);
+                        f2.setStart(genStart);
+                        f2.setEnd(genEnd);
+                        if (f2 instanceof BasicFeature) {
+                            BasicFeature bf = (BasicFeature) f;
+                            BasicFeature bf2 = (BasicFeature) f2;
+                            bf2.setThickEnd((int) ((offset + bf.getThickEnd()) / 1000));
+                            bf2.setThickStart((int) ((offset + bf.getThickStart()) / 1000));
+                        }
+                        f2.setName(f.getName());
+                        chrAllFeatures.add(f2);
+
+                        lastFeaturePosition = genEnd;
+                    }
+                }
+            }
+        }
+
+        setFeatures(Globals.CHR_ALL, chrAllFeatures);
+    }
+
+
+    class CoverageDataSource extends AbstractDataSource {
+
+        int windowSize = 1000;
+        double dataMin = 0;
+        double dataMax = 0;
+
+        Map<String, DataTile> dataCache = new HashMap();
+
+        protected int getNumZoomLevels(String chr) {
+            return 0;
+        }
+
+        protected DataTile getRawData(String chr, int startLocation, int endLocation) {
+
+
+            DataTile coverageData = dataCache.get(chr);
+            if (coverageData == null) {
+                coverageData = computeCoverage(chr, startLocation, endLocation);
+                dataCache.put(chr, coverageData);
+            }
+            return coverageData;
+
+        }
+
+        @Override
+        public int getLongestFeature(String chr) {
+            return windowSize;
+        }
+
+        public double getDataMax() {
+            return dataMax;
+        }
+
+        public double getDataMin() {
+            return dataMin;
+        }
+
+        public TrackType getTrackType() {
+            return TrackType.OTHER;  //To change body of implemented methods use File | Settings | File Templates.
+        }
+
+        // This won't work for large track!
+
+        private DataTile computeCoverage(String chr, int start, int end) {
+
+            int nBins = (end - start) / windowSize + 1;
+            int[] starts = new int[nBins];
+            int[] ends = new int[nBins];
+            for (int i = 0; i < nBins; i++) {
+                starts[i] = start + i * windowSize;
+                ends[i] = starts[i] + windowSize;
+            }
+            float[] values = new float[nBins];
+            Iterator<Feature> features = featureMap.get(chr).iterator();
+            if (features != null) {
+                while (features.hasNext()) {
+                    Feature f = features.next();
+                    int startBin = f.getStart() / windowSize;
+                    int endBin = f.getEnd() / windowSize;
+                    for (int i = startBin; i < endBin; i++) {
+                        values[i] = values[i] + 1;
+                        dataMax = Math.max(dataMax, values[i]);
+                    }
+                }
+            }
+            return new DataTile(starts, ends, values, null);
+
+        }
+
+        public String getValueString(String chr, double position) {
+
+            int zoom = Math.max(0, ViewContext.getInstance().getZoom());
+            List<LocusScore> scores = getSummaryScoresForRange(chr, (int) position - 10, (int) position + 10, zoom);
+
+            // give a 2 pixel window, otherwise very narrow features will be missed.
+            double bpPerPixel = ViewContext.getInstance().getScale();
+            double minWidth = 2 * bpPerPixel;    /* * */
+
+            if (scores == null) {
+                return "";
+            } else {
+                LocusScore score = (LocusScore) FeatureUtils.getFeatureAt(position, minWidth, scores);
+                return score == null ? "" : "Mean count: " + score.getScore();
+            }
+        }
+
+        protected void computeGenomeCoverage() {
+            int nBins = 1000;
+            int[] starts = new int[nBins];
+            int[] ends = new int[nBins];
+            float[] values = new float[nBins];
+            Arrays.fill(values, 0);
+
+            Genome currentGenome = ViewContext.getInstance().getGenome();
+            double step = ((double) currentGenome.getLength() / 1000) / nBins;
+            for (int i = 0; i < nBins; i++) {
+                starts[i] = (int) (i * step);
+                ends[i] = (int) ((i + 1) * step);
+            }
+
+
+            for (String chr : currentGenome.getChromosomeNames()) {
+                List<Feature> features = featureMap.get(chr);
+                if (features != null) {
+                    long offset = currentGenome.getCumulativeOffset(chr);
+                    for (Feature f : features) {
+                        int genStart = (int) ((offset + f.getStart()) / 1000);
+                        int genEnd = (int) ((offset + f.getEnd()) / 1000);
+                        int binStart = (int) (genStart / step);
+                        int binEnd = (int) (genEnd / step);
+                        for (int i = binStart; i <= binEnd; i++) {
+                            values[i] = values[i] + 1;
+                            dataMax = Math.max(dataMax, values[i]);
+                        }
+
+                    }
+                }
+            }
+
+            dataCache.put(Globals.CHR_ALL, new DataTile(starts, ends, values, null));
+        }
+    }
+
+
+}
diff --git a/src/org/broad/igv/track/FeatureDirSource.java b/src/org/broad/igv/track/FeatureDirSource.java
new file mode 100644
index 0000000..8c491dd
--- /dev/null
+++ b/src/org/broad/igv/track/FeatureDirSource.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track;
+
+import org.apache.log4j.Logger;
+import org.broad.igv.feature.AbstractFeatureParser;
+import org.broad.igv.feature.Feature;
+import org.broad.igv.feature.FeatureParser;
+import org.broad.igv.feature.LocusScore;
+import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.util.MessageUtils;
+import org.broad.igv.util.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * User: jrobinso
+ * Date: Jan 31, 2010
+ */
+public class FeatureDirSource implements FeatureSource {
+
+    static Logger log = Logger.getLogger(FeatureDirSource.class);
+    LRUCache<String, List<Feature>> featureCache;
+    Properties fileMap;
+    String rootDir;
+    ResourceLocator rootLocator;
+
+    public FeatureDirSource(ResourceLocator locator) throws IOException {
+        featureCache = new LRUCache(this, 3);
+        rootLocator = locator;
+        setRootDir(locator.getPath());
+
+        fileMap = new Properties();
+        InputStream propStream = ParsingUtils.openInputStream(locator);
+        fileMap.load(propStream);
+        propStream.close();
+
+
+    }
+
+    boolean featuresLoading = false;
+    public List<Feature> getFeatures(final String chr) {
+
+        List<Feature> features = featureCache.get(chr);
+        if (features == null) {
+            final String filename = fileMap.getProperty(chr);
+            if (filename != null && !featuresLoading) {
+                featuresLoading = true;
+                NamedRunnable runnable = new NamedRunnable() {
+                    AsciiLineReader reader = null;
+
+                    public void run() {
+                        String path = rootDir + "/" + filename;
+                        try {
+                            log.info("Loading " + path);
+                            // Load features here
+                            ResourceLocator loc = new ResourceLocator(rootLocator.getServerURL(), path);
+
+                            FeatureParser fp = AbstractFeatureParser.getInstanceFor(loc);
+                            reader = ParsingUtils.openAsciiReader(loc);
+                            List<Feature> features = fp.loadFeatures(reader);
+                            featureCache.put(chr, features);
+                            IGVMainFrame.getInstance().repaintDataPanels();
+                        } catch (IOException ex) {
+                            MessageUtils.showMessage("Error loading file: " + path + " (" + ex.toString() + ")");
+                            log.info("Error loading feature file: " + filename, ex);
+                        } finally {
+                            if (reader != null) {
+                                reader.close();
+                            }
+                            featuresLoading = false;
+                        }
+                    }
+
+                    public String getName() {
+                        return "FeatureDirSource.getFeatures()";
+                    }
+                };
+                runnable.run();
+                //LongRunningTask.submit(runnable);
+            }
+        }
+        return featureCache.get(chr);
+    }
+
+    public List<LocusScore> getCoverageScores(String chr, int i, int i1, int zoom) {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public int getBinSize() {
+        return 0;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public void setBinSize(int size) {
+        // ignored
+    }
+
+
+    public Iterator<Feature> getFeatures(String chr, int start, int end) {
+        List<Feature> features = getFeatures(chr);
+        return features == null ? null : features.iterator();
+    }
+
+    private void setRootDir(String path) {
+
+        if (path.startsWith("http:") || path.startsWith("https:") || path.startsWith("file:")) {
+            int idx = path.lastIndexOf('/');
+            rootDir = path.substring(0, idx);
+        } else {
+            rootDir = (new File(path)).getParent();
+        }
+
+    }
+}
diff --git a/src/org/broad/igv/track/FeatureDirTrack.java b/src/org/broad/igv/track/FeatureDirTrack.java
deleted file mode 100644
index 0e69249..0000000
--- a/src/org/broad/igv/track/FeatureDirTrack.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-package org.broad.igv.track;
-
-import org.apache.log4j.Logger;
-import org.broad.igv.feature.AbstractFeatureParser;
-import org.broad.igv.feature.Feature;
-import org.broad.igv.feature.FeatureParser;
-import org.broad.igv.util.ParsingUtils;
-import org.broad.igv.util.AsciiLineReader;
-import org.broad.igv.util.ObjectCache;
-import org.broad.igv.util.ResourceLocator;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Properties;
-
-/**
- * @author jrobinso
- */
-public class FeatureDirTrack extends AbstractFeatureTrack {
-
-    static Logger log = Logger.getLogger(FeatureDirTrack.class);
-    ObjectCache<String, List<Feature>> featureCache;
-    List<Feature> wholeGenomeFeatures = new ArrayList();
-    Properties fileMap;
-    File rootDir;
-    ResourceLocator rootLocator;
-
-    public FeatureDirTrack(ResourceLocator locator, String name) throws IOException {
-        super(locator, name);
-        featureCache = new ObjectCache(3);
-        rootLocator = locator;
-        rootDir = (new File(locator.getPath())).getParentFile();
-
-        fileMap = new Properties();
-        InputStream propStream = ParsingUtils.openInputStream(locator);
-        fileMap.load(propStream);
-        propStream.close();
-
-
-    }
-
-    @Override
-    public List<Feature> getFeatures(String chr) {
-        List<Feature> features = featureCache.get(chr);
-        if (features == null) {
-            String filename = fileMap.getProperty(chr);
-            if (filename != null) {
-                AsciiLineReader reader = null;
-                try {
-                    File featureFile = new File(rootDir, filename);
-                    // Load features here
-                    ResourceLocator loc = new ResourceLocator(rootLocator.getServerURL(), featureFile.getAbsolutePath());
-
-                    FeatureParser fp = AbstractFeatureParser.getInstanceFor(loc);
-                    reader = ParsingUtils.openAsciiReader(loc);
-                    features = fp.loadFeatures(reader);
-                    featureCache.put(chr, features);
-                    reader.close();
-                } catch (IOException ex) {
-                    log.info("Error loading feature file: " + filename, ex);
-                } finally {
-                    if (reader != null) {
-                        reader.close();
-                    }
-                }
-
-
-            }
-
-        }
-        return features;
-    }
-
-    @Override
-    public void setFeatures(String chr, List<Feature> features) {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-}
-
diff --git a/src/org/broad/igv/track/FeatureSource.java b/src/org/broad/igv/track/FeatureSource.java
new file mode 100644
index 0000000..e1e5e10
--- /dev/null
+++ b/src/org/broad/igv/track/FeatureSource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track;
+
+import org.broad.igv.feature.Feature;
+import org.broad.igv.feature.LocusScore;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * User: jrobinso
+ * Date: Jan 31, 2010
+ */
+public interface FeatureSource {
+
+    /**
+     * Return an iterator over all features that overlap the interval
+     * <p/>
+     * // TODO -- change List to Iterator
+     *
+     * @param chr
+     * @param start
+     * @param end
+     * @return
+     */
+    public Iterator<Feature> getFeatures(String chr, int start, int end) throws IOException;
+
+
+    List<LocusScore> getCoverageScores(String chr, int i, int i1, int zoom);
+
+    int getBinSize();
+
+    void setBinSize(int size);
+}
diff --git a/src/org/broad/igv/track/FeatureTrack.java b/src/org/broad/igv/track/FeatureTrack.java
index 82ff6b1..e1266d8 100644
--- a/src/org/broad/igv/track/FeatureTrack.java
+++ b/src/org/broad/igv/track/FeatureTrack.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -15,123 +15,855 @@
  * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
  * FOREGOING.
  */
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
 package org.broad.igv.track;
 
-//~--- non-JDK imports --------------------------------------------------------
-
 import org.apache.log4j.Logger;
-import org.broad.igv.feature.Feature;
-import org.broad.igv.feature.FeatureUtils;
+import org.broad.igv.Globals;
+import org.broad.igv.PreferenceManager;
+import org.broad.igv.feature.*;
+import org.broad.igv.renderer.*;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.UIConstants;
+import org.broad.igv.ui.WaitCursorManager;
+import org.broad.igv.ui.util.MessageUtils;
+import org.broad.igv.util.BrowserLauncher;
+import org.broad.igv.util.LongRunningTask;
+import org.broad.igv.util.NamedRunnable;
 import org.broad.igv.util.ResourceLocator;
 
 import java.awt.*;
-import java.util.ArrayList;
-import java.util.HashMap;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.*;
 import java.util.List;
-import java.util.Map;
 
 /**
  * @author jrobinso
  */
-public class FeatureTrack extends AbstractFeatureTrack {
+public class FeatureTrack extends AbstractTrack {
 
-    private static Logger log = Logger.getLogger(DataTrack.class);
-    /**
-     * Map of chromosome -> feature list
-     */
-    private Map<String, List<Feature>> featureMap;
+    private static Logger log = Logger.getLogger(FeatureTrack.class);
+    static int maxLevels = 200;
 
-    /**
-     * NOTE:  Ths constructor apparently assumes feature lists are already sorted.
-     *
-     * @param locator
-     * @param name
-     */
-    public FeatureTrack(ResourceLocator locator, String name) {
-        super(locator, name);
+    private boolean expanded;
+    private List<Rectangle> featureRects = new ArrayList();
+    private PackedFeatures packedFeatures;
+
+    private FeatureRenderer renderer;
+    private DataRenderer coverageRenderer;
+
+    // true == features,  false =  coverage
+    private boolean showFeatures = true;
+
+    protected FeatureSource source;
+    private static final int MINIMUM_FEATURE_SPACING = 1;
+    private int visibilityWindow = -1;
+
+    private Rectangle expandButtonRect = expandButtonRect = new Rectangle();
+    private static final int EXPAND_ICON_BUFFER_WIDTH = 17;
+    private static final int EXPAND_ICON_BUFFER_HEIGHT = 17;
+    public static final int MARGIN = 5;
+    public static final String FEATURE_VISIBILITY_WINDOW = "featureVisibilityWindow";
+
+
+    public FeatureTrack(String id, FeatureSource source) {
+        super(id);
+        init(source);
+    }
+
+
+    public FeatureTrack(ResourceLocator locator, FeatureSource source) {
+        super(locator);
+        init(source);
+        this.getMinimumHeight();
+    }
+
+    private void init(FeatureSource source) {
+        this.expanded = PreferenceManager.getInstance().isExpandTracks();
+        this.source = source;
         setMinimumHeight(10);
         setColor(Color.blue.darker());
+        coverageRenderer = new HeatmapRenderer();
+
+        if (source.getBinSize() > 0) {
+            visibilityWindow = source.getBinSize();
+        }
+    }
+
+    @Override
+    public void setTrackProperties(TrackProperties trackProperties) {
+        super.setTrackProperties(trackProperties);
+
+        if (trackProperties.getFeatureVisibilityWindow() >= 0) {
+            setVisibilityWindow(trackProperties.getFeatureVisibilityWindow());
+        }
+
+    }
+
+    @Override
+    public int getHeight() {
+
+        if (false == isVisible()) {
+            return 0;
+        }
+        return super.getHeight() * Math.max(1, getNumberOfFeatureLevels());
+    }
+
+    public int getNumberOfFeatureLevels() {
+
+        return expanded ? (packedFeatures == null ? 1 : packedFeatures.getRowCount()) : 1;
     }
 
     /**
-     * NOTE:  Ths constructor apparently assumes feature lists are already sorted.
+     * Return a score over the interval.  This is required by the track interface to support sorting.
      *
-     * @param locator
-     * @param name
+     * @param chr   ignored
+     * @param start ignored
+     * @param end   ignored
+     * @param zoom  ignored
+     * @param type  ignored
+     * @return 0
      */
-    public FeatureTrack(ResourceLocator locator, String name, List<Feature> allFeatures) {
-        super(locator, name);
-        initFeatures(allFeatures);
-        setMinimumHeight(10);
-        setColor(Color.blue.darker());
+    public float getRegionScore(String chr, int start, int end, int zoom, RegionScoreType type) {
+        return -Float.MAX_VALUE;
+    }
+
+
+    public FeatureRenderer getRenderer() {
+        if (renderer == null) {
+            setRendererClass(BasicFeatureRenderer.class);
+        }
+        return renderer;
     }
 
     /**
-     * NOTE:  Ths constructor apparently assumes feature lists are already sorted.
+     * Return a string for popup text.
      *
-     * @param locator
-     * @param name
-     * @param features
+     * @param chr
+     * @param position -- 1 relative coordinates
+     * @param y
+     * @return
      */
-    public FeatureTrack(ResourceLocator locator, String name, Map<String, List<Feature>> features) {
-        super(locator, name);
-        setMinimumHeight(10);
-        setColor(Color.blue.darker());
-        this.featureMap = features;
+    public String getValueStringAt(String chr, double position, int y) {
+
+
+        if (showFeatures) {
+            // Reverse lookup, get mouse X position from mouse.  Consider refactoring to get it directly
+            double scale = ViewContext.getInstance().getScale();
+            double origin = ViewContext.getInstance().getOrigin();
+            int x = (int) ((position - origin) / scale);
+            if (x < EXPAND_ICON_BUFFER_WIDTH) {
+                if (expandButtonRect.contains(x, y)) {
+                    return "<b><strong>Click to " + (isExpanded() ? " collapse track " : " expand track</strong></b>");
+                } else {
+                    return "";
+                }
+            }
+
+
+            Feature feature = getFeatureAt(chr, position, y);
+
+            //getRenderer().setHighlightFeature(feature);
+
+            return (feature == null) ? null : feature.getValueString(position, null);
+        } else {
+            int zoom = Math.max(0, ViewContext.getInstance().getZoom());
+            List<LocusScore> scores = source.getCoverageScores(chr, (int) position - 10, (int) position + 10, zoom);
+
+            if (scores == null) {
+                return "";
+            } else {
+                // give a 2 pixel window, otherwise very narrow features will be missed.
+                double bpPerPixel = ViewContext.getInstance().getScale();
+                double minWidth = MINIMUM_FEATURE_SPACING * bpPerPixel;    /* * */
+                LocusScore score = (LocusScore) FeatureUtils.getFeatureAt(position, minWidth, scores);
+                return score == null ? "" : "Mean count: " + score.getScore();
+            }
 
-        // If the # of contigs is < 100 sample features for a whole genome view
-        if (features.size() < 100) {
-            sampleGenomeFeatures();
         }
     }
 
+    private Feature getFeatureAt(String chr, double position, int y) {
 
-    public void initFeatures(List<Feature> allFeatures) {
-        // Separate features by chromosome
+        if (packedFeatures == null) {
+            return null;
+        }
 
-        featureMap = new HashMap();
-        for (Feature f : allFeatures) {
-            List<Feature> fList = getFeatures(f.getChromosome());
-            if (fList == null) {
-                fList = new ArrayList();
-                featureMap.put(f.getChromosome(), fList);
+        Feature feature = null;
+        // Determine the level number (for expanded tracks.
+        int levelNumber = 0;
+        if (featureRects != null) {
+            for (int i = 0; i < featureRects.size(); i++) {
+                Rectangle r = featureRects.get(i);
+                if ((y >= r.y) && (y <= r.getMaxY())) {
+                    levelNumber = i;
+                    break;
+                }
             }
-            fList.add(f);
         }
 
-        for (List<Feature> featureList : featureMap.values()) {
-            FeatureUtils.sortFeatureList(featureList);
+        int nLevels = this.getNumberOfFeatureLevels();
+        List<Feature> features = null;
+        if ((nLevels > 1) && (levelNumber < nLevels)) {
+            features = packedFeatures.rows.get(levelNumber).features;
+        } else {
+            features = packedFeatures.features;
         }
-        if (featureMap.size() < 100) {
-            sampleGenomeFeatures();
+        if (features != null) {
+
+            // give a 2 pixel window, otherwise very narrow features will be missed.
+            double bpPerPixel = ViewContext.getInstance().getScale();
+            double minWidth = MINIMUM_FEATURE_SPACING * bpPerPixel;
+            feature = (Feature) FeatureUtils.getFeatureAt(position, minWidth, features);
         }
+        return feature;
+    }
 
+    public WindowFunction getWindowFunction() {
+        return WindowFunction.count;
+    }
+
+    @Override
+    public boolean handleClick(MouseEvent e) {
+
+        if (e.getClickCount() == 1 && !e.isShiftDown() && !e.isPopupTrigger() && e.getButton() == MouseEvent.BUTTON1) {
+            if (e.getX() < EXPAND_ICON_BUFFER_WIDTH) {
+                if (expandButtonRect.contains(e.getPoint())) {
+                    setExpanded(!expanded);
+                    IGVMainFrame.getInstance().doResizeTrackPanels();
+                    IGVMainFrame.getInstance().doRefresh();
+                }
+                return true;
+            }
 
+
+            Feature f = getFeatureAtMousePosition(e);
+
+            if (f != null) {
+                String url = f.getURL();
+                if (url == null) {
+                    String trackURL = getUrl();
+                    if (trackURL != null && f.getIdentifier() != null) {
+                        String encodedID = URLEncoder.encode(f.getIdentifier());
+                        url = trackURL.replaceAll("\\$\\$", encodedID);
+                    }
+                }
+                // A scheduler is used so that the browser opening can be canceled in the event of a double
+                // click.  In that case the first click will schedule the browser opening, but it is delayed
+                // long enough to enable the second click to cancel it.
+                if (url != null) {
+                    scheduleBrowserTask(url, UIConstants.getDoubleClickInterval());
+                    e.consume();
+                    return true;
+                }
+            }
+
+        } else
+
+        {
+            // This call will cancel opening a browser in the event of a double click
+            cancelBrowserTask();
+        }
+
+        return false;
     }
 
-    public void setFeatures(String chr, List<Feature> features) {
-        setFeatures(chr, features, false);
+    public Feature getFeatureAtMousePosition(MouseEvent e) {
+        double location = getViewContext().getChromosomePosition(e.getX());
+        double displayLocation = location + 1;
+        Feature f = getFeatureAt(getViewContext().getChrName(), displayLocation, e.getY());
+        return f;
+    }
+
+
+    @Override
+    public boolean isExpanded() {
+        return expanded;
+    }
+
+    /**
+     * Required by the interface, really not applicable to feature tracks
+     */
+    public boolean isLogNormalized() {
+        return true;
+    }
+
+
+    public void overlay(RenderContext context, Rectangle rect) {
+        getRenderer().setOverlayMode(true);
+        renderFeatures(context, rect);
+    }
+
+    public void render(RenderContext context, Rectangle rect) {
+        getRenderer().setOverlayMode(false);
+        Rectangle renderRect = new Rectangle(rect);
+        renderRect.y = renderRect.y + MARGIN;
+        renderRect.height -= MARGIN;
+
+
+        double windowSize = rect.width * context.getScale();
+
+        int vw = getVisibilityWindow();
+        showFeatures = true;
+        if (vw == 0) {
+            showFeatures = !context.getChr().equals(Globals.CHR_ALL);
+        } else if (vw > 0) {
+            showFeatures = !context.getChr().equals(Globals.CHR_ALL) && windowSize <= vw;
+        }
+
+        if (showFeatures) {
+            renderFeatures(context, renderRect);
+        } else {
+            renderCoverage(context, renderRect);
+        }
+        boolean showIcon = PreferenceManager.getInstance().getBooleanPreference(PreferenceManager.SHOW_EXPAND_ICON, true);
+        if (showIcon && !IGVMainFrame.getInstance().isExportingSnapshot()) {
+            renderExpandTool(context, rect);
+        }
+    }
+
+    private void renderCoverage(RenderContext context, Rectangle inputRect) {
+        List<LocusScore> scores = source.getCoverageScores(context.getChr(), (int) context.getOrigin(),
+                (int) context.getEndLocation(), context.getZoom());
+        if (scores == null) {
+            Graphics2D g = context.getGraphic2DForColor(Color.gray);
+            Rectangle textRect = new Rectangle(inputRect);
+
+            // Keep text near the top of the track rectangle
+            textRect.height = Math.min(inputRect.height, 20);
+            String message = context.getChr().equals(Globals.CHR_ALL) ? "Zoom in to see features." :
+                    "Zoom in to see features, or right-click to increase Feature Visibility Window.";
+            GraphicUtils.drawCenteredText(message, textRect, g);
+        } else {
+            float max = getMaxEstimate(scores);
+            ContinuousColorScale cs = getColorScale();
+            if (cs != null) {
+                cs.setPosEnd(max);
+            }
+            setDataRange(new DataRange(0, 0, max));
+            coverageRenderer.render(this, scores, context, inputRect);
+        }
+    }
+
+
+    private float getMaxEstimate(List<LocusScore> scores) {
+        float max = 0;
+        int n = Math.min(200, scores.size());
+        for (int i = 0; i < n; i++) {
+            max = Math.max(max, scores.get(i).getScore());
+        }
+        return max;
+    }
+
+
+    private boolean featuresLoading = false;
+
+
+    private int[] p1 = new int[3];
+    private int[] p2 = new int[3];
+
+    private void renderExpandTool(RenderContext contect, Rectangle rect) {
+
+        if (packedFeatures == null || packedFeatures.getRowCount() <= 1) {
+            return;
+        }
+
+        Graphics2D g2d = contect.getGraphic2DForColor(Color.DARK_GRAY);
+        int levelHeight = getHeight() / this.getNumberOfFeatureLevels() + 1;
+
+        g2d.clearRect(rect.x, rect.y, EXPAND_ICON_BUFFER_WIDTH, levelHeight);
+
+        expandButtonRect.x = rect.x + 3;
+        expandButtonRect.y = rect.y + MARGIN + 4;
+        expandButtonRect.width = 10;
+        expandButtonRect.height = 10;
+
+        if (expanded) {
+            p1[0] = expandButtonRect.x;
+            p1[1] = expandButtonRect.x + 8;
+            p1[2] = expandButtonRect.x + 4;
+            p2[0] = expandButtonRect.y;
+            p2[1] = expandButtonRect.y;
+            p2[2] = expandButtonRect.y + 8;
+            g2d.fillPolygon(p1, p2, 3);
+
+        } else {
+            p1[0] = expandButtonRect.x;
+            p1[1] = expandButtonRect.x + 8;
+            p1[2] = expandButtonRect.x;
+            p2[0] = expandButtonRect.y;
+            p2[1] = expandButtonRect.y + 4;
+            p2[2] = expandButtonRect.y + 8;
+            g2d.fillPolygon(p1, p2, 3);
+        }
+
+    }
+
+
+    // Render features in the given input rectangle.
+
+    private void renderFeatures(RenderContext context, Rectangle inputRect) {
+
+        if (featuresLoading) {
+            return;
+        }
+
+        //if (log.isDebugEnabled()) {
+        //    log.debug("renderFeatures: " + getName());
+        //}
+
+        String chr = context.getChr();
+        int start = (int) context.getOrigin();
+        int end = (int) context.getEndLocation() + 1;
+        if (packedFeatures == null || !packedFeatures.containsInterval(chr, start, end)) {
+            featuresLoading = true;
+            int maxEnd = end;
+            Genome genome = ViewContext.getInstance().getGenome();
+            if (genome != null) {
+                Chromosome c = genome.getChromosome(chr);
+                if (c != null) maxEnd = Math.max(c.getLength(), end);
+            }
+            int delta = (end - start) / 2;
+            int expandedStart = Math.max(0, start - delta);
+            int expandedEnd = Math.min(maxEnd, end + delta);
+
+            loadFeatures(chr, expandedStart, expandedEnd);
+
+            if (!IGVMainFrame.getInstance().isExportingSnapshot()) {
+                return;
+            }
+        }
+
+
+        if (expanded) {
+            List<FeatureRow> rows = packedFeatures.rows;
+            if (rows != null && rows.size() > 0) {
+
+                int nLevels = rows.size();
+                synchronized (featureRects) {
+
+                    featureRects.clear();
+
+                    // Divide rectangle into equal height levels
+                    double h = inputRect.getHeight() / nLevels;
+                    Rectangle rect = new Rectangle(inputRect.x, inputRect.y, inputRect.width, (int) h);
+                    for (FeatureRow row : rows) {
+                        featureRects.add(new Rectangle(rect));
+                        getRenderer().renderFeatures(row.features, context, rect, this);
+                        rect.y += h;
+                    }
+                }
+            }
+        } else {
+            List<Feature> features = packedFeatures.features;
+            if (features != null) {
+                getRenderer().renderFeatures(features, context, inputRect, this);
+            }
+        }
+
+
+    }
+
+    /**
+     * Loads and segregates features into rows such that they do not overlap.  Loading is done in a background
+     * thread.
+     *
+     * @param chr
+     * @param start
+     * @param end
+     */
+    private void loadFeatures(final String chr, final int start, final int end) {
+
+        NamedRunnable runnable = new NamedRunnable() {
+            public void run() {
+                try {
+
+                    // TODO -- implement source to return iterators
+                    Iterator<Feature> iter = source.getFeatures(chr, start, end);
+                    if (iter == null) {
+                        packedFeatures = new PackedFeatures(chr, start, end);
+                    } else {
+                        packedFeatures = new PackedFeatures(chr, start, end, iter, getName());
+                    }
+
+                    IGVMainFrame.getInstance().repaint();
+                } catch (Throwable e) {
+                    // Mark the interval with an empty feature list to prevent an endless loop of load
+                    // attempts.
+                    packedFeatures = new PackedFeatures(chr, start, end);
+                    String msg = "Error loading features for interval: " +
+                            chr + ":" + start + "-" + end + " <br>" + e.toString();
+                    MessageUtils.showMessage(msg);
+                    log.error(msg, e);
+                }
+
+                finally {
+                    featuresLoading = false;
+                    if (log.isDebugEnabled()) {
+                        log.debug("features loaded");
+                    }
+                }
+            }
+
+            public String getName() {
+                return "Load features: " + FeatureTrack.this.getName();
+            }
+        };
+
+        LongRunningTask.submit(runnable);
+    }
+
+
+    @Override
+    public void setExpanded(boolean value) {
+        expanded = value;
+    }
+
+    @Override
+    public void setHeight(int newHeight) {
+
+        int levelCount = this.getNumberOfFeatureLevels();
+        super.setHeight(Math.max(getMinimumHeight(), newHeight / levelCount));
+    }
+
+    public void setRendererClass(Class rc) {
+        try {
+            renderer = (FeatureRenderer) rc.newInstance();
+        } catch (Exception ex) {
+            log.error("Error instatiating renderer ", ex);
+        }
+    }
+
+    public void setStatType(WindowFunction type) {
     }
 
     /**
      * Method description
      *
+     * @param zoom
+     */
+    public void setZoom(int zoom) {
+    }
+
+    /**
+     * A timer task is used for opening web links to distinguish a click from a double click.
+     */
+    private TimerTask currentBrowserTask = null;
+
+    private void cancelBrowserTask() {
+        if (currentBrowserTask != null) {
+            currentBrowserTask.cancel();
+            currentBrowserTask = null;
+        }
+    }
+
+    private void scheduleBrowserTask(final String url, int delay) {
+        cancelBrowserTask();
+        currentBrowserTask = new TimerTask() {
+            public void run() {
+                try {
+                    BrowserLauncher.openURL(url);
+                } catch (IOException e1) {
+                    log.error("Error opening url: " + url);
+                }
+            }
+        };
+        (new java.util.Timer()).schedule(currentBrowserTask, delay);
+    }
+
+
+    /**
+     * Return the nextLine or previous feature relative to the center location.
+     * TODO -- investigate delegating this method to FeatureSource, where it might be possible to simplify the implementation
+     *
      * @param chr
-     * @param features
+     * @param center
+     * @param forward
+     * @return
+     * @throws IOException
      */
-    public void setFeatures(String chr, List<Feature> features, boolean sorted) {
-        if (!sorted) {
-            FeatureUtils.sortFeatureList(features);
+    public Feature nextFeature(String chr, double center, boolean forward) throws IOException {
+
+        Feature f = null;
+        ViewContext vc = ViewContext.getInstance();
+        boolean canScroll = (forward && !vc.windowAtEnd()) ||
+                (!forward && vc.getOrigin() > 0);
+
+        if (packedFeatures != null && packedFeatures.containsInterval(chr, (int) center - 1, (int) center + 1)) {
+            if (packedFeatures.features.size() > 0 && canScroll) {
+                f = (Feature)
+                        (forward ? FeatureUtils.getFeatureAfter(center + 1, packedFeatures.features) :
+                                FeatureUtils.getFeatureBefore(center - 1, packedFeatures.features));
+            }
+
+            if (f == null) {
+                int binSize = source.getBinSize();
+
+                if (forward == true) {
+                    // Forward
+                    int nextStart = packedFeatures.end;
+                    String nextChr = chr;
+                    while (nextChr != null) {
+                        int chrLength = vc.getGenome().getChromosome(nextChr).getLength();
+                        while (nextStart < chrLength) {
+                            int nextEnd = binSize > 0 ? nextStart + source.getBinSize() : chrLength;
+                            Iterator<Feature> iter = source.getFeatures(nextChr, nextStart, nextEnd);
+
+                            // The check on position should not be neccessary, but not all implementations of getFeatures
+                            // obey the contract to return features only in the interval.
+                            if (iter != null) {
+                                while (iter.hasNext()) {
+                                    Feature feat = iter.next();
+                                    if (feat.getStart() > nextStart) {
+                                        return feat;
+                                    }
+                                }
+                            }
+                            nextStart = nextEnd;
+                        }
+                        nextChr = vc.getNextChrName(nextChr);
+                        nextStart = 0;
+                    }
+                } else {
+                    // Reverse
+                    int nextEnd = packedFeatures.start;
+                    String nextChr = chr;
+                    while (nextChr != null) {
+                        while (nextEnd > 0) {
+                            int nextStart = binSize > 0 ? Math.max(0, nextEnd - source.getBinSize()) : 0;
+                            Iterator<Feature> iter = source.getFeatures(nextChr, nextStart, nextEnd);
+                            if (iter != null && iter.hasNext()) {
+                                Feature prevFeature = null;
+                                while (iter.hasNext()) {
+                                    Feature feat = iter.next();
+                                    if (feat.getStart() < nextEnd) {
+                                        prevFeature = feat;
+                                    }
+                                }
+                                if (prevFeature != null) {
+                                    return prevFeature;
+                                }
+                            }
+                            nextEnd = nextStart;
+                        }
+                        nextChr = vc.getPrevChrName(nextChr);
+                        if (nextChr != null) {
+                            nextEnd = vc.getGenome().getChromosome(nextChr).getLength();
+                        }
+                    }
+                }
+            }
+        }
+
+        return f;
+    }
+
+    public int getVisibilityWindow() {
+        return visibilityWindow;
+    }
+
+    public void setVisibilityWindow(int windowSize) {
+        this.visibilityWindow = windowSize;
+        source.setBinSize(visibilityWindow);
+    }
+
+    @Override
+    public void restorePersistentState(Map<String, String> attributes) {
+        super.restorePersistentState(attributes);    //To change body of overridden methods use File | Settings | File Templates.
+
+        String fvw = attributes.get(FEATURE_VISIBILITY_WINDOW);
+        if (fvw != null) {
+            try {
+                visibilityWindow = Integer.parseInt(fvw);
+            } catch (NumberFormatException e) {
+                log.error("Error restoring visibilityWindow: " + fvw);
+            }
+        }
+
+    }
+
+    @Override
+    public Map<String, String> getPersistentState() {
+        Map<String, String> stateMap = super.getPersistentState();
+        stateMap.put(FEATURE_VISIBILITY_WINDOW, String.valueOf(visibilityWindow));
+        return stateMap;
+
+    }
+
+    //public Feature nextFeature(String chr, double position, boolean forward) {
+//
+//    return source.nextFeature(chr, position, forward);
+//}
+
+
+    static class FeatureRow {
+        int start;
+        int end;
+        List<Feature> features;
+
+        FeatureRow() {
+            this.features = new ArrayList(100);
+        }
+
+        void addFeature(Feature feature) {
+            if (features.isEmpty()) {
+                this.start = feature.getStart();
+            }
+            features.add(feature);
+            end = feature.getEnd();
         }
-        resetLevelList();
-        featureMap.put(chr, features);
     }
 
-    public List<Feature> getFeatures(String chr) {
-        return featureMap.get(chr);
+
+    static class PackedFeatures {
+        private String trackName;
+        private String chr;
+        private int start;
+        private int end;
+        private List<Feature> features;
+        private List<FeatureRow> rows;
+
+        PackedFeatures(String chr, int start, int end) {
+            this.chr = chr;
+            this.start = start;
+            this.end = end;
+            features = Collections.emptyList();
+            rows = Collections.emptyList();
+        }
+
+        PackedFeatures(String chr, int start, int end, Iterator<Feature> iter, String trackName) {
+            this.trackName = trackName;
+            this.chr = chr;
+            this.start = start;
+            this.end = end;
+            features = new ArrayList(1000);
+            rows = packFeatures(iter);
+        }
+
+        int getRowCount() {
+            return rows.size();
+        }
+
+        boolean containsInterval(String chr, int start, int end) {
+            return this.chr.equals(chr) && start >= this.start && end <= this.end;
+        }
+
+        /**
+         * Allocates each alignment to the rows such that there is no overlap.
+         *
+         * @param iter TabixLineReader wrapping the collection of alignments
+         */
+        List<FeatureRow> packFeatures(Iterator<Feature> iter) {
+
+            List<FeatureRow> rows = new ArrayList(10);
+            if (iter == null || !iter.hasNext()) {
+                return rows;
+            }
+
+
+            // Compares 2 alignments by length.
+            Comparator lengthComparator = new Comparator<Feature>() {
+                public int compare(Feature row1, Feature row2) {
+                    return (row2.getEnd() - row2.getStart()) - (row1.getEnd() - row2.getStart());
+                }
+            };
+
+            Feature firstFeature = iter.next();
+            features.add(firstFeature);
+            int totalCount = 1;
+
+            LinkedHashMap<Integer, PriorityQueue<Feature>> bucketArray = new LinkedHashMap();
+
+            while (iter.hasNext()) {
+                Feature feature = iter.next();
+                features.add(feature);
+
+                int bucketNumber = feature.getStart();
+
+                PriorityQueue bucket = bucketArray.get(bucketNumber);
+                if (bucket == null) {
+                    bucket = new PriorityQueue(5, lengthComparator);
+                    bucketArray.put(bucketNumber, bucket);
+                }
+                bucket.add(feature);
+                totalCount++;
+
+            }
+
+            // Allocate alignments to rows
+            FeatureRow currentRow = new FeatureRow();
+            currentRow.addFeature(firstFeature);
+            int allocatedCount = 1;
+            int nextStart = currentRow.end + MINIMUM_FEATURE_SPACING;
+
+
+            int lastKey = 0;
+            int lastAllocatedCount = 0;
+            while (allocatedCount < totalCount && rows.size() < maxLevels) {
+
+                // Check to prevent infinite loops
+                if (lastAllocatedCount == allocatedCount) {
+                    String msg = "Infinite loop detected while packing features for track: " + trackName +
+                            ".<br>Not all features will be shown." +
+                            "<br>Please contact igv-help at broadinstitute.org";
+
+                    log.error(msg);
+                    MessageUtils.showMessage(msg);
+                    break;
+                }
+                lastAllocatedCount = allocatedCount;
+
+                // Next row Loop through alignments until we reach the end of the interval
+
+                PriorityQueue<Feature> bucket = null;
+                // Advance to nextLine occupied bucket
+
+                ArrayList<Integer> emptyBucketKeys = new ArrayList();
+                for (Integer key : bucketArray.keySet()) {
+                    //if (key < lastKey) {
+                    //    String msg = "Features from track: " + trackName + " are not sorted.  Some features might not be shown.<br>" +
+                    //            "Please notify igv-help at broadinstitute.org";
+                    //    MessageUtils.showMessage(msg);
+                    //}
+                    lastKey = key;
+                    if (key >= nextStart) {
+                        bucket = bucketArray.get(key);
+
+                        Feature feature = bucket.poll();
+
+                        if (bucket.isEmpty()) {
+                            emptyBucketKeys.add(key);
+                        }
+                        currentRow.addFeature(feature);
+                        nextStart = currentRow.end + MINIMUM_FEATURE_SPACING;
+                        allocatedCount++;
+                    }
+                }
+                for (Integer key : emptyBucketKeys) {
+                    bucketArray.remove(key);
+                }
+
+
+                // We've reached the end of the interval,  start a new row
+                if (currentRow.features.size() > 0) {
+                    rows.add(currentRow);
+                    lastAllocatedCount = 0;
+                }
+                currentRow = new FeatureRow();
+                nextStart = 0;
+                lastKey = 0;
+
+
+            }
+            // Add the last row
+            if (currentRow.features.size() > 0) {
+                rows.add(currentRow);
+            }
+
+            return rows;
+        }
     }
 }
+
diff --git a/src/org/broad/igv/track/FeatureTrackUtils.java b/src/org/broad/igv/track/FeatureTrackUtils.java
new file mode 100644
index 0000000..7583684
--- /dev/null
+++ b/src/org/broad/igv/track/FeatureTrackUtils.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: May 15, 2010
+ * Time: 3:49:38 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class FeatureTrackUtils {
+
+
+    //public static double estimateFeatureDensity(FeatureSource source) {
+    //
+    //
+    //}
+}
diff --git a/src/org/broad/igv/track/GeneTrack.java b/src/org/broad/igv/track/GeneTrack.java
index d79d91b..902ac26 100644
--- a/src/org/broad/igv/track/GeneTrack.java
+++ b/src/org/broad/igv/track/GeneTrack.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -27,8 +27,8 @@ import org.broad.igv.util.ResourceLocator;
 
 import java.awt.*;
 import java.awt.event.MouseEvent;
+import java.io.IOException;
 import java.util.Collection;
-import java.util.List;
 import java.util.Map;
 
 /**
@@ -124,13 +124,21 @@ public class GeneTrack implements Track {
         sequenceTrack.render(context, rect);
     }
 
+    public String getId() {
+        return featureTrack.getId();
+    }
+
     /**
      * Method description
      *
      * @return
      */
-    public String getId() {
-        return featureTrack.getId();
+    public String getName() {
+        return featureTrack.getName();
+    }
+
+    public String getSampleId() {
+        return featureTrack.getSampleId();
     }
 
     /**
@@ -139,7 +147,7 @@ public class GeneTrack implements Track {
      * @return
      */
     public String getDisplayName() {
-        return featureTrack.getDisplayName();
+        return featureTrack.getName();
     }
 
     /**
@@ -374,6 +382,10 @@ public class GeneTrack implements Track {
         return featureTrack.isLogNormalized();
     }
 
+    public boolean isShowDataRange() {
+        return featureTrack.isShowDataRange();
+    }
+
     /**
      * Method description
      *
@@ -411,6 +423,14 @@ public class GeneTrack implements Track {
         featureTrack.refreshData(timestamp);
     }
 
+    public void setFontSize(int h) {
+        featureTrack.setFontSize(h);
+    }
+
+    public int getFontSize() {
+        return featureTrack.getFontSize();
+    }
+
     /**
      * Method description
      *
@@ -480,27 +500,45 @@ public class GeneTrack implements Track {
      *
      * @param name
      */
-    public void setDisplayName(String name) {
-        featureTrack.setDisplayName(name);
+    public void setName(String name) {
+        featureTrack.setName(name);
     }
 
-    /**
-     * Method description
-     */
-    public void updateProperties() {
-        featureTrack.updateProperties();
+    public void setUrl(String url) {
+        featureTrack.setUrl(url);
     }
 
+
     public void preloadData(String chr, int start, int end, int zoom) {
         featureTrack.preloadData(chr, start, end, zoom);
     }
 
-    public Map<String, String> getSessionAttributes() {
-        return featureTrack.getSessionAttributes();
+    public Feature getFeatureAtMousePosition(MouseEvent e) {
+        return featureTrack.getFeatureAtMousePosition(e);
+    }
+
+    public String getId_142() {
+        return featureTrack.getId_142();
+    }
+
+    public void setSampleId(String sampleId) {
+        featureTrack.setSampleId(sampleId);
     }
 
-    public void update(Map<String, String> attributes) {
-        featureTrack.update(attributes);
+    public float logScaleData(float dataY) {
+        return featureTrack.logScaleData(dataY);
+    }
+
+    public Map<String, String> getPersistentState() {
+        return featureTrack.getPersistentState();
+    }
+
+    public void restorePersistentState(Map<String, String> attributes) {
+        featureTrack.restorePersistentState(attributes);
+    }
+
+    public String getUrl() {
+        return featureTrack.getUrl();
     }
 
     public void renderName(Graphics2D graphics, Rectangle rect, Rectangle visibleRect) {
@@ -523,15 +561,15 @@ public class GeneTrack implements Track {
         featureTrack.setColorScale(colorScale);
     }
 
-    public String getActualDisplayName() {
-        return featureTrack.getActualDisplayName();
-    }
+    //public String getActualName() {
+    //    return featureTrack.getActualName();
+    //}
 
     public ContinuousColorScale getColorScale() {
         return featureTrack.getColorScale();
     }
 
-    public List<Feature> getFeatures(String chr) {
-        return featureTrack.getFeatures(chr);
-    }
+    public Feature nextFeature(String chr, double center, boolean forward) throws IOException {
+        return featureTrack.nextFeature(chr, center, forward);
+   }
 }
diff --git a/src/org/broad/igv/track/GisticTrack.java b/src/org/broad/igv/track/GisticTrack.java
index 269e397..b8c0910 100644
--- a/src/org/broad/igv/track/GisticTrack.java
+++ b/src/org/broad/igv/track/GisticTrack.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -36,7 +36,7 @@ import org.broad.igv.feature.LocusScore;
 import org.broad.igv.renderer.ColorScale;
 import org.broad.igv.renderer.DataRange;
 import org.broad.igv.renderer.GisticTrackRenderer;
-import org.broad.igv.ui.IGVModel;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.util.ResourceLocator;
 
 import java.awt.*;
@@ -50,6 +50,8 @@ import java.util.Map;
  */
 public class GisticTrack extends AbstractTrack {
 
+    private static final int DEFAULT_HEIGHT = 50;
+    
     private double maxQValue = 0;
 
     private double maxGScore = 0;
@@ -68,13 +70,13 @@ public class GisticTrack extends AbstractTrack {
      * Creates a new instance of GisticScoreList
      *
      * @param locator
-     * @param name
      */
-    public GisticTrack(ResourceLocator locator, String name) {
-        super(locator, name);
+    public GisticTrack(ResourceLocator locator) {
+        super(locator);
         ampScoreMap = new HashMap<String, List<GisticScore>>();
         delScoreMap = new HashMap<String, List<GisticScore>>();
         renderer = new GisticTrackRenderer();
+        setHeight(DEFAULT_HEIGHT);
 
         // renderer = new GisticTrackRenderer();
     }
@@ -362,7 +364,7 @@ public class GisticTrack extends AbstractTrack {
     public String getValueStringAt(String chr, double position, int ignore) {
 
         // give a 2 pixel window, otherwise very narrow features will be missed.
-        double bpPerPixel = IGVModel.getInstance().getViewContext().getScale();
+        double bpPerPixel = ViewContext.getInstance().getScale();
         double minWidth = 2 * bpPerPixel;    /*
                                               */
 
diff --git a/src/org/broad/igv/track/HDFDataTrack.java b/src/org/broad/igv/track/HDFDataTrack.java
index 80a2bad..ad8647e 100644
--- a/src/org/broad/igv/track/HDFDataTrack.java
+++ b/src/org/broad/igv/track/HDFDataTrack.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -28,7 +28,6 @@ package org.broad.igv.track;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
 import org.broad.igv.data.DataSource;
 import org.broad.igv.data.HDFDataManager;
 import org.broad.igv.data.HDFDataSource;
@@ -36,7 +35,7 @@ import org.broad.igv.feature.Feature;
 import org.broad.igv.feature.LocusScore;
 import org.broad.igv.h5.ObjectNotFoundException;
 import org.broad.igv.renderer.DataRange;
-import org.broad.igv.ui.IGVModel;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.util.ResourceLocator;
 
 import java.awt.*;
@@ -65,26 +64,27 @@ public class HDFDataTrack extends DataTrack {
      * @param locator
      * @param name
      * @param trackNumber
-     * @param scoreType
      * @throws FileNotFoundException
      */
-    public HDFDataTrack(HDFDataManager dataManager, ResourceLocator locator,
-                        String name, int trackNumber)
+    public HDFDataTrack(HDFDataManager dataManager,
+                        ResourceLocator locator,
+                        String name,
+                        int trackNumber)
             throws FileNotFoundException {
 
-        super(locator, name);
+        super(locator, name + trackNumber, name);
         //this.windowFunction = scoreType;
         this.trackNumber = trackNumber;
 
         dataSource = new HDFDataSource(dataManager, name, trackNumber);
 
-        String chr = IGVModel.getInstance().getViewContext().getChrName();
+        String chr = ViewContext.getInstance().getChrName();
 
         // By default autoscale so that (1) lower limit is zero,  or (2) scale
         // is symetrical
 
-        float max = (float) dataSource.getDataMax(IGVConstants.CHR_ALL);
-        float min = Math.min(0, (float) dataSource.getDataMin(IGVConstants.CHR_ALL));
+        float max = (float) dataSource.getDataMax();
+        float min = Math.min(0, (float) dataSource.getDataMin());
         if (min < 0) {
             float absMax = Math.max(Math.abs(max), Math.abs(min));
             min = -absMax;
@@ -110,7 +110,7 @@ public class HDFDataTrack extends DataTrack {
 
         // Temporary color hack until attribute file supports color
 
-        String tmp = getDisplayName();
+        String tmp = getName();
         if (tmp.contains("K4")) {
             setColor(K4_COLOR);
         } else if (tmp.contains("K9")) {
diff --git a/src/org/broad/igv/track/HDFListTrack.java b/src/org/broad/igv/track/HDFListTrack.java
deleted file mode 100644
index 2d32ee2..0000000
--- a/src/org/broad/igv/track/HDFListTrack.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-package org.broad.igv.track;
-
-import org.apache.log4j.Logger;
-import org.broad.igv.data.HDFDataManager;
-import org.broad.igv.data.HDFDataManagerFactory;
-import org.broad.igv.feature.Feature;
-import org.broad.igv.feature.LocusScore;
-import org.broad.igv.util.ParsingUtils;
-import org.broad.igv.util.AsciiLineReader;
-import org.broad.igv.util.ObjectCache;
-import org.broad.igv.util.ResourceLocator;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.*;
-
-/**
- * @author jrobinso
- *         <p/>
- *         TODO -- generalize this
- */
-public class HDFListTrack extends DataTrack {
-
-    static Logger log = Logger.getLogger(HDFListTrack.class);
-    ObjectCache<String, List<Feature>> featureCache;
-    List<Feature> wholeGenomeFeatures = new ArrayList();
-    Map<String, ResourceLocator> locatorMap;
-    Map<String, HDFDataTrack> trackMap = new HashMap();
-    File rootDir;
-    ResourceLocator rootLocator;
-
-    public HDFListTrack(ResourceLocator locator, String name) throws IOException {
-        super(locator, name);
-        featureCache = new ObjectCache(3);
-        rootLocator = locator;
-        rootDir = (new File(locator.getPath())).getParentFile();
-
-        init(locator);
-    }
-
-    //  chr    path   server
-    private void init(ResourceLocator locator) {
-        locatorMap = new HashMap();
-        AsciiLineReader reader = null;
-        try {
-            reader = ParsingUtils.openAsciiReader(locator);
-            String nextLine;
-            while ((nextLine = reader.readLine()) != null) {
-                if (nextLine.startsWith("track")) {
-                    TrackProperties tp = new TrackProperties();
-                    ParsingUtils.parseTrackLine(nextLine, tp);
-                    setTrackProperties(tp);
-
-                }
-                String[] tokens = nextLine.split("\t");
-                if (tokens.length < 2) {
-                    //   log.info("Unexpected # of tokens in file: " + locator.getPath() + " => " + nextLine);
-                } else {
-                    String chr = tokens[0].trim();
-                    String path = tokens[1].trim();
-                    String server = null;
-                    if (tokens.length > 2) {
-                        server = tokens[2].trim();
-                    }
-                    locatorMap.put(chr, new ResourceLocator(server, path));
-                }
-            }
-
-        } catch (IOException ex) {
-            log.error("Error parsing sam file group file", ex);
-            throw new RuntimeException("Error parsing file: " + locator.getPath() + " (" +
-                    ex.getMessage());
-        } finally {
-            if (reader != null) {
-                reader.close();
-            }
-        }
-
-    }
-
-    private HDFDataTrack loadTrack(String chr) throws FileNotFoundException {
-
-        ResourceLocator locator = locatorMap.get(chr);
-        if (locator == null) {
-            return null;
-        }
-
-        HDFDataManager dataManager = HDFDataManagerFactory.getDataManager(locator);
-        TrackProperties trackProperties = dataManager.getTrackProperties();
-
-        HDFDataTrack track = new HDFDataTrack(dataManager, locator, this.getDisplayName(), 0);
-        if (trackProperties != null) {
-            track.setTrackProperties(trackProperties);
-        }
-        return track;
-    }
-
-    // TODO -- get window functions dynamically from data
-    static List<WindowFunction> wfs = new ArrayList();
-
-
-    static {
-        wfs.add(WindowFunction.percentile10);
-        wfs.add(WindowFunction.median);
-        wfs.add(WindowFunction.mean);
-        wfs.add(WindowFunction.percentile90);
-        wfs.add(WindowFunction.max);
-
-    }
-
-    @Override
-    public Collection<WindowFunction> getAvailableWindowFunctions() {
-        return wfs;
-    }
-
-    @Override
-    public List<LocusScore> getSummaryScores(String chr, int startLocation, int endLocation, int zoom) {
-
-        HDFDataTrack t = trackMap.get(chr);
-        if (t == null) {
-            try {
-                t = loadTrack(chr);
-                trackMap.put(chr, t);
-            } catch (FileNotFoundException fileNotFoundException) {
-                log.error("Error loading track", fileNotFoundException);
-            }
-        }
-        return t == null ? new ArrayList() : t.getSummaryScores(chr, startLocation, endLocation, zoom);
-    }
-
-    public void setStatType(WindowFunction type) {
-        for (HDFDataTrack t : trackMap.values()) {
-            t.setStatType(type);
-        }
-    }
-
-    public WindowFunction getWindowFunction() {
-        return trackMap.values().iterator().next().getWindowFunction();
-    }
-
-    public boolean isLogNormalized() {
-        return trackMap.values().iterator().next().isLogNormalized();
-    }
-
-    public float getRegionScore(String chr, int start, int end, int zoom, RegionScoreType type) {
-        HDFDataTrack t = trackMap.get(chr);
-        return t == null ? 0.0f : t.getRegionScore(chr, start, end, zoom, type);
-    }
-}
-
diff --git a/src/org/broad/igv/track/IndexedBEDFeatureSource.java b/src/org/broad/igv/track/IndexedBEDFeatureSource.java
new file mode 100644
index 0000000..e01b11e
--- /dev/null
+++ b/src/org/broad/igv/track/IndexedBEDFeatureSource.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track;
+
+import org.apache.log4j.Logger;
+import org.broad.igv.feature.BEDFileParser;
+import org.broad.igv.feature.Feature;
+import org.broad.igv.feature.Genome;
+import org.broad.igv.feature.LocusScore;
+import org.broad.igv.sam.reader.FeatureIndex;
+import org.broad.igv.sam.reader.SamUtils;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.ui.WaitCursorManager;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * User: jrobinso
+ * Date: Apr 21, 2010
+ */
+public class IndexedBEDFeatureSource implements FeatureSource {
+
+    static Logger log = Logger.getLogger(IndexedBEDFeatureSource.class);
+
+    File bedFile;
+    FeatureIndex featureIndex;
+    BEDFileParser parser;
+    HashMap<String, String> chrMappings = new HashMap();
+
+    public IndexedBEDFeatureSource(File samFile, File indexFile) {
+        this.bedFile = samFile;
+        if (indexFile.exists()) {
+            featureIndex = new FeatureIndex(indexFile);
+        }
+        parser = new BEDFileParser();
+        initChrMap();
+    }
+
+    private void initChrMap() {
+        if (featureIndex != null) {
+            Genome genome = ViewContext.getInstance().getGenome();
+            if (genome != null) {
+                Set<String> seqNames = featureIndex.getIndexedChromosomes();
+                if (seqNames != null) {
+                    for (String chr : seqNames) {
+                        String alias = genome.getChromosomeAlias(chr);
+                        chrMappings.put(alias, chr);
+                    }
+
+                }
+            }
+        }
+    }
+
+    public Iterator<Feature> getFeatures(String chr, int start, int end) {
+        return loadFeatures(chr, start, end).iterator();
+    }
+
+    public List<LocusScore> getCoverageScores(String chr, int i, int i1, int zoom) {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public int getBinSize() {
+        return 0;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public void setBinSize(int size) {
+        // ignored
+    }
+
+    private FeatureIndex getIndex() {
+        if (featureIndex == null) {
+            featureIndex = SamUtils.getIndexFor(bedFile.getAbsolutePath());
+        }
+        return featureIndex;
+    }
+
+    public Set<String> getSequenceNames() {
+        FeatureIndex idx = getIndex();
+        if (idx == null) {
+            return null;
+        } else {
+            return idx.getIndexedChromosomes();
+        }
+
+    }
+
+
+    private List<Feature> loadFeatures(String chr, int start, int end) {
+
+        final List<Feature> features = new ArrayList();
+        if (featureIndex == null) {
+            featureIndex = SamUtils.getIndexFor(bedFile.getAbsolutePath());
+        }
+
+        if (featureIndex == null) {
+            throw new UnsupportedOperationException("SAM files must be indexed to support query methods");
+        }
+        
+        String chrAlias = chrMappings.containsKey(chr) ? chrMappings.get(chr) : chr;
+        
+        if (!featureIndex.containsChromosome(chrAlias)) {
+            return features;
+        }
+        WaitCursorManager.CursorToken ct = WaitCursorManager.showWaitCursor();
+        try {
+
+
+            // If contained == false (include overlaps) we need to adjust the start to
+            // ensure we get features that extend into this segment.
+            int startAdjustment = featureIndex.getLongestFeature(chrAlias);
+            int startTileNumber = Math.max(0, (start - startAdjustment)) / featureIndex.getTileWidth();
+
+            FeatureIndex.TileDef seekPos = featureIndex.getTileDef(chrAlias, startTileNumber);
+
+            if (seekPos != null) {
+                FileInputStream is = null;
+                try {
+                    // Skip to the start of the chromosome (approximate)
+                    is = new FileInputStream(bedFile);
+                    is.getChannel().position(seekPos.getStartPosition());
+
+                    BufferedReader br = new BufferedReader(new InputStreamReader(is));
+                    String nextLine = "";
+                    while ((nextLine = br.readLine()) != null) {
+                        String[] tokens = nextLine.split("\t");
+                        Feature f = parser.parseLine(tokens, tokens.length);
+                        if (f.getStart() > end || !f.getChr().equals(chrAlias)) {
+                            break;
+                        } else if (f.getEnd() < start) {
+                            continue;
+                        }
+                        features.add(f);
+                    }
+
+
+                } catch (IOException ex) {
+                    log.error("Error opening sam file", ex);
+                }
+                finally {
+                    if (is != null) {
+                        try {
+                            is.close();
+                        } catch (IOException e) {
+                        }
+                    }
+                }
+            }
+        } finally {
+            WaitCursorManager.removeWaitCursor(ct);
+        }
+        return features;
+    }
+
+    public boolean hasIndex() {
+        if (featureIndex == null) {
+            featureIndex = SamUtils.getIndexFor(bedFile.getAbsolutePath());
+        }
+        return featureIndex != null;
+    }
+
+
+}
diff --git a/src/org/broad/igv/track/MultiFileWrapper.java b/src/org/broad/igv/track/MultiFileWrapper.java
index 5e27094..65b6bdb 100644
--- a/src/org/broad/igv/track/MultiFileWrapper.java
+++ b/src/org/broad/igv/track/MultiFileWrapper.java
@@ -1,7 +1,25 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
 package org.broad.igv.track;
 
-import org.broad.igv.util.ParsingUtils;
 import org.broad.igv.util.AsciiLineReader;
+import org.broad.igv.util.ParsingUtils;
 import org.broad.igv.util.ResourceLocator;
 
 import java.io.IOException;
diff --git a/src/org/broad/igv/track/RegionScoreType.java b/src/org/broad/igv/track/RegionScoreType.java
index 48d9af3..f95a6ce 100644
--- a/src/org/broad/igv/track/RegionScoreType.java
+++ b/src/org/broad/igv/track/RegionScoreType.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -27,5 +27,5 @@ package org.broad.igv.track;
  * @author jrobinso
  */
 public enum RegionScoreType {
-    AMPLIFICATION, DELETION, EXPRESSION, SCORE
+    AMPLIFICATION, DELETION, EXPRESSION, SCORE, FLUX
 }
diff --git a/src/org/broad/igv/track/RenderContext.java b/src/org/broad/igv/track/RenderContext.java
index 08b05b4..92678a3 100644
--- a/src/org/broad/igv/track/RenderContext.java
+++ b/src/org/broad/igv/track/RenderContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -31,23 +31,23 @@ import java.util.Map;
  */
 public class RenderContext {
 
+    private String genomeId;
     private String chr;
     private double startLocation;
     private double endLocation;
     private int zoom;
-    private double originShift;
     private double scale;
     private Graphics2D graphics;
     private Map<Color, Graphics2D> graphicCacheByColor;
     Rectangle visibleRect;
 
-    public RenderContext(String chr, double startLocation, double endLocation,
-                         int zoom, double originShift, double scale, Rectangle visibleRect,
+    public RenderContext(String genomeId, String chr, double startLocation, double endLocation,
+                         int zoom, double scale, Rectangle visibleRect,
                          Graphics2D graphics) {
+        this.genomeId = genomeId;
         this.chr = chr;
         this.startLocation = startLocation;
         this.endLocation = endLocation;
-        this.originShift = originShift;
         this.zoom = zoom;
         this.scale = scale;
         this.graphics = graphics;
@@ -70,7 +70,7 @@ public class RenderContext {
         return chr;
     }
 
-    public double getStartLocation() {
+    public double getOrigin() {
         return startLocation;
     }
 
@@ -78,10 +78,6 @@ public class RenderContext {
         return endLocation;
     }
 
-    public double getOrigin() {
-        return originShift;
-    }
-
     public double getScale() {
         return scale;
     }
@@ -107,10 +103,22 @@ public class RenderContext {
     @Override
     protected void finalize() throws Throwable {
         super.finalize();
+        dispose();
+    }
+
+    public void dispose() {
         for (Graphics2D g : graphicCacheByColor.values()) {
             g.dispose();
         }
+        graphicCacheByColor.clear();
     }
 
 
+    public String getGenomeId() {
+        return genomeId;
+    }
+
+    public void setGenomeId(String genomeId) {
+        this.genomeId = genomeId;
+    }
 }
diff --git a/src/org/broad/igv/track/SequenceTrack.java b/src/org/broad/igv/track/SequenceTrack.java
index 505b5de..76fd4b0 100644
--- a/src/org/broad/igv/track/SequenceTrack.java
+++ b/src/org/broad/igv/track/SequenceTrack.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -21,11 +21,11 @@ package org.broad.igv.track;
 
 //~--- non-JDK imports --------------------------------------------------------
 
+import org.broad.igv.Globals;
 import org.broad.igv.renderer.ColorScale;
 import org.broad.igv.renderer.ContinuousColorScale;
 import org.broad.igv.renderer.Renderer;
 import org.broad.igv.renderer.SequenceRenderer;
-import org.broad.igv.IGVConstants;
 
 import java.awt.*;
 
@@ -42,12 +42,8 @@ public class SequenceTrack extends AbstractTrack {
 
     SequenceRenderer sequenceRenderer = new SequenceRenderer();
     ColorScale colorScale;
+    private boolean showColorSpace = false;
 
-    /**
-     * Constructs ...
-     *
-     * @param name
-     */
     public SequenceTrack(String name) {
         super(name);
     }
@@ -62,8 +58,8 @@ public class SequenceTrack extends AbstractTrack {
 
         // Are we zoomed in far enough to show the sequence?  Scale is
         // in BP / pixel,  need at least 1 pixel.
-        if (context.getScale() < 1 && !context.getChr().equals(IGVConstants.CHR_ALL)) {
-            sequenceRenderer.draw(context, rect);
+        if (context.getScale() < 1 && !context.getChr().equals(Globals.CHR_ALL)) {
+            sequenceRenderer.draw(context, rect, showColorSpace);
         }
 
     }
@@ -72,107 +68,57 @@ public class SequenceTrack extends AbstractTrack {
         //To change body of implemented methods use File | Settings | File Templates.
     }
 
-    /**
-     * Method description
-     *
-     * @param type
-     */
+
     public void setStatType(WindowFunction type) {
 
         // ignore
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public WindowFunction getWindowFunction() {
 
         // ignore
         return null;
     }
 
-    /**
-     * Method description
-     *
-     * @param rc
-     */
+
     public void setRendererClass(Class rc) {
 
         // ignored
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public Renderer getRenderer() {
         return sequenceRenderer;
     }
 
-    /**
-     * // TODO -- this reference to the view context is ugly. Is there some other way to
-     * // do this?
-     *
-     * @return
-     */
+
     @Override
     public int getHeight() {
-
-        // boolean showSequence = IGVModel.getInstance().getViewContext().getScale()  < 1;
-        // return showSequence ? SEQUENCE_HEIGHT : 0;
-        return SEQUENCE_HEIGHT;
+        return SEQUENCE_HEIGHT * (showColorSpace ? 2 : 1);
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public boolean isLogNormalized() {
         return true;
     }
 
-    /**
-     * Method description
-     *
-     * @param chr
-     * @param position
-     * @param y
-     * @return
-     */
+
     public String getValueStringAt(String chr, double position, int y) {
 
-        // TOTO -- return sequence at this position
+        // TODO -- return sequence at this position
         return "";
     }
 
-    /**
-     * Method description
-     *
-     * @param chr
-     * @param start
-     * @param end
-     * @param zoom
-     * @param type
-     * @return
-     */
+
     public float getRegionScore(String chr, int start, int end, int zoom, RegionScoreType type) {
         return 0;
     }
 
-    /**
-     * Method description
-     *
-     * @param x
-     * @param y
-     */
+
     public boolean handleClick(int x, int y) {
 
         // Ignore
         return false;
     }
 
+
 }
diff --git a/src/org/broad/igv/track/TestUtils.java b/src/org/broad/igv/track/TestUtils.java
index 59d5f4b..9edf450 100644
--- a/src/org/broad/igv/track/TestUtils.java
+++ b/src/org/broad/igv/track/TestUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/track/Track.java b/src/org/broad/igv/track/Track.java
index c4bff28..824e9cf 100644
--- a/src/org/broad/igv/track/Track.java
+++ b/src/org/broad/igv/track/Track.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,23 +23,24 @@ package org.broad.igv.track;
 
 //~--- non-JDK imports --------------------------------------------------------
 
+import org.broad.igv.feature.Feature;
 import org.broad.igv.renderer.ContinuousColorScale;
 import org.broad.igv.renderer.DataRange;
 import org.broad.igv.renderer.Renderer;
 import org.broad.igv.util.ResourceLocator;
+import org.broad.igv.session.Persistable;
 
 import java.awt.*;
 import java.awt.event.MouseEvent;
 import java.util.Collection;
-import java.util.Map;
 
 /**
  * @author jrobinso
  */
-public interface Track {
+public interface Track extends Persistable {
 
     /**
-     * Render the track contects in the rectangle
+     * Render the track contents in the rectangle
      *
      * @param context
      * @param rect
@@ -62,7 +63,11 @@ public interface Track {
      */
     public void renderAxis(RenderContext context, Rectangle rect);
 
-    public void renderName(Graphics2D graphics, Rectangle rect, Rectangle visibleRect);
+    /**
+     * Render the track name
+     * */
+    
+    public void renderName(Graphics2D graphics, Rectangle trackRectangle, Rectangle visibleRectangle);
 
     /**
      * Render this track as an overlay in the track rectangle
@@ -73,86 +78,43 @@ public interface Track {
     public void overlay(RenderContext context, Rectangle rect);
 
     /**
-     * Method description
-     *
-     * @return
+     * @return  The unique identifier for the track.  This should only be used in saving/restoring sessions.
      */
     public String getId();
 
-    public void setDisplayName(String name);
-
     /**
-     * Method description
-     *
+     *  Get the display name of the track.
      * @return
      */
-    public String getDisplayName();
-
-    public String getActualDisplayName();
+    public String getName();
 
     /**
-     * Method description
      *
-     * @return
+     * @return  ID for use with the sample info table.  Not
      */
+    public String getSampleId();
+
+
+    public void setName(String name);
+
+    public void setUrl(String url);
+
     public ResourceLocator getResourceLocator();
 
-    /**
-     * Method description
-     *
-     * @param key
-     * @param value
-     */
     public void setAttributeValue(String key, String value);
 
-    /**
-     * Method description
-     *
-     * @param attributeKey
-     * @return
-     */
     public String getAttributeValue(String attributeKey);
 
-    /**
-     * Method description
-     *
-     * @param isVisible
-     */
     public void setVisible(boolean isVisible);
 
-    /**
-     * An inactive track should be treated as though it does not exist
-     *
-     * @return
-     */
     public boolean isVisible();
 
-    /**
-     * Set whether the track is visible or not if
-     *
-     * @param overlayVisible
-     */
     public void setOverlayVisible(boolean overlayVisible);
 
-    /**
-     * Method description
-     *
-     * @param type
-     */
     public void setTrackType(TrackType type);
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public TrackType getTrackType();
 
-    /**
-     * Method description
-     *
-     * @param preferredHeight
-     */
     public void setHeight(int preferredHeight);
 
     public void setTop(int top);
@@ -163,50 +125,20 @@ public interface Track {
 
     public ContinuousColorScale getColorScale();
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public int getHeight();
 
     public int getPreferredHeight();
 
     public int getMinimumHeight();
 
-    /**
-     * Method description
-     *
-     * @param axisDefinition
-     */
     public void setDataRange(DataRange axisDefinition);
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public DataRange getDataRange();
 
-    /**
-     * Return the color of this track, which might be the default
-     *
-     * @return
-     */
     public Color getColor();
 
-    /**
-     * Method description
-     *
-     * @param color
-     */
     public void setColor(Color color);
 
-    /**
-     * Return the color of this track, which might be the default
-     *
-     * @return
-     */
     public Color getAltColor();
 
     public void setAltColor(Color color);
@@ -215,103 +147,33 @@ public interface Track {
 
     public void setMidColor(Color color);
 
-    /**
-     * Method description
-     *
-     * @param type
-     */
     public void setStatType(WindowFunction type);
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public WindowFunction getWindowFunction();
 
-    /**
-     * Method description
-     *
-     * @param rc
-     */
     public void setRendererClass(Class rc);
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public Renderer getRenderer();
 
-    public void updateProperties();
-
-    /**
-     * Method description
-     *
-     * @param selected
-     */
     public void setSelected(boolean selected);
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public boolean isSelected();
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public boolean isDraggable();
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public boolean isLogNormalized();
 
-    // public List<Feature> getFeatures(String chr, int startLocation, int endLocation);
+    public boolean isShowDataRange();
 
-    // public int getNumberOfFeatureLevels();
-
-    // public List<LocusScore> getSummaryScores(String chr, int startLocation, int endLocation, int zoom);
-
-    /**
-     * Method description
-     *
-     * @param chr
-     * @param position
-     * @param y
-     * @return
-     */
     public String getValueStringAt(String chr, double position, int y);
 
-    /**
-     * Method description
-     *
-     * @param chr
-     * @param start
-     * @param end
-     * @param zoom
-     * @param type
-     * @return
-     */
     public float getRegionScore(String chr, int start, int end, int zoom, RegionScoreType type);
 
-    /**
-     * Enable/Disables the track for feature that can be shown at
-     * different levels with in a track.
-     *
-     * @param timestamp
-     */
+    public void refreshData(long timestamp);
 
-    // public void setMultiLevelFeatures(boolean isMultiLevel);
+    public void setFontSize(int h);
 
-    // public boolean isMuliLevelFeatures();
-    public void refreshData(long timestamp);
+    public int getFontSize();
 
     /**
      * Record the file from which this track was created.
@@ -327,14 +189,9 @@ public interface Track {
      */
     public String getSourceFile();
 
-    /**
-     * @param value
-     */
+
     public void setExpanded(boolean value);
 
-    /**
-     * @return
-     */
     public boolean isExpanded();
 
 
@@ -344,18 +201,11 @@ public interface Track {
 
     public void setTrackProperties(TrackProperties trackProperties);
 
-    /**
-     * Called to force track to preload data prior to a repaint.
-     *
-     * @param chr
-     * @param start
-     * @param end
-     */
-    public void preloadData(String chr, int start, int end, int zoom);
+    Feature getFeatureAtMousePosition(MouseEvent e);
 
+    public String getId_142();
 
-    // Session stuff
-    public Map<String, String> getSessionAttributes();
+    void setSampleId(String sampleId);
 
-    public void update(Map<String, String> values);
+    float logScaleData(float dataY);
 }
diff --git a/src/org/broad/igv/track/TrackGroup.java b/src/org/broad/igv/track/TrackGroup.java
index 2122e1b..af95f0d 100644
--- a/src/org/broad/igv/track/TrackGroup.java
+++ b/src/org/broad/igv/track/TrackGroup.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -24,8 +24,16 @@ package org.broad.igv.track;
 
 //~--- JDK imports ------------------------------------------------------------
 
-import java.util.ArrayList;
-import java.util.Collection;
+import org.broad.igv.renderer.GraphicUtils;
+import org.broad.igv.sam.AlignmentTrack;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.ui.FontManager;
+import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.RegionOfInterest;
+
+import java.awt.*;
+import java.awt.geom.Rectangle2D;
+import java.util.*;
 import java.util.List;
 
 /**
@@ -39,27 +47,21 @@ public class TrackGroup {
     /**
      * Key used to group tracks (e.g. SAMPLE_ID).
      */
-    String attributeKey;
-    private String attributeValue;
+    private String name;
     private boolean drawBorder = true;
-    List<Track> tracks;
+    private List<Track> tracks;
+    private boolean selected;
 
-    /**
-     * Constructs ...
-     */
+    
     public TrackGroup() {
-        this("", "");
+        this("");
     }
 
     /**
-     * Constructs ...
-     *
-     * @param attributeKey
      * @param attributeValue
      */
-    public TrackGroup(String attributeKey, String attributeValue) {
-        this.attributeKey = attributeKey;
-        this.attributeValue = attributeValue;
+    public TrackGroup(String attributeValue) {
+        this.name = attributeValue;
         tracks = new ArrayList<Track>();
     }
 
@@ -148,23 +150,7 @@ public class TrackGroup {
         tracks.addAll(index, trackList);
     }
 
-    /**
-     * Remove all tracks in supplied list. The list is rebuilt for
-     * effeciency, 1 pass is needed rather than 2 and the membership
-     * test on a set is very fast.
-     *
-     * @param tracksToRemove
-     */
-    public void removeAll(Collection<Track> tracksToRemove) {
-        tracks.removeAll(tracksToRemove);
-
-    }
 
-    /**
-     * Method description
-     *
-     * @param track
-     */
     public void remove(Track track) {
         tracks.remove(track);
     }
@@ -178,26 +164,7 @@ public class TrackGroup {
     }
 
     /**
-     * Returns a representative value for an attribute.  Its not clear what
-     * the rule for groups should be, for now just takes the first non-null
-     * value from a track.
-     *
-     * @param attributeKey
-     * @return
-     */
-    public String getAttributeValue(String attributeKey) {
-
-        for (Track track : tracks) {
-            String value = track.getAttributeValue(attributeKey);
-            if ((value != null) && !value.equals(attributeKey)) {
-                return value;
-            }
-        }
-        return "";
-    }
-
-    /**
-     * Return a composite score for the entire group.  For now use the maximum
+     * Return a composite score for the entire group.  For now use the maximum track
      * score.   Note that scores for tracks not appropriate to the score type will
      * return -Float.MAX, so they are effectively ignored.
      *
@@ -211,18 +178,21 @@ public class TrackGroup {
     public float getRegionScore(String chr, int start, int end, int zoom, RegionScoreType type) {
         float score = -Float.MAX_VALUE;
         for (Track track : tracks) {
-            score = Math.max(score, track.getRegionScore(chr, start, end, zoom, type));
+            if (track.isVisible()) {
+                score = Math.max(score, track.getRegionScore(chr, start, end, zoom, type));
+            }
         }
         return score;
     }
 
+
     /**
      * Method description
      *
      * @return
      */
-    public String getAttributeValue() {
-        return attributeValue;
+    public String getName() {
+        return name;
     }
 
     /**
@@ -256,4 +226,334 @@ public class TrackGroup {
         }
         return false;
     }
+
+    public int getHeight() {
+
+        int h = 0;
+        for (Track track : tracks) {
+            if (track.isVisible()) {
+                h += track.getHeight();
+            }
+        }
+        return h;
+    }
+
+    public void renderName(Graphics2D g2D, Rectangle rect, boolean isSelected) {
+
+        // Calculate fontsize
+        int gap = Math.min(4, rect.height / 3);
+        int prefHeight = rect.height - gap;
+        int fontSize = Math.max(6, Math.min(14, prefHeight));
+
+        Font font = FontManager.getScalableFont(Font.BOLD, fontSize);
+        g2D.setFont(font);
+        FontManager.applyScalableTextHints(g2D);
+
+        GraphicUtils.drawWrappedText(getName(), rect, g2D, true);
+
+    }
+
+    /**
+     * Sort tracks by the array of attribute names.
+     *
+     * @param attributeNames
+     * @param ascending
+     */
+
+    public void sortByAttributes(final String attributeNames[],
+                                 final boolean[] ascending) {
+        if ((tracks != null) && !tracks.isEmpty()) {
+            Comparator comparator = new Comparator() {
+
+                public int compare(Object arg0, Object arg1) {
+                    Track t1 = (Track) arg0;
+                    Track t2 = (Track) arg1;
+
+                    for (int i = 0; i < attributeNames.length; i++) {
+                        String attName = attributeNames[i];
+
+                        if (attName != null) {
+                            String value1 = t1.getAttributeValue(attName);
+
+                            if (value1 == null) {
+                                value1 = "";
+                            }
+
+                            value1 = value1.toLowerCase();
+                            String value2 = t2.getAttributeValue(attName);
+
+                            if (value2 == null) {
+                                value2 = "";
+                            }
+                            value2 = value2.toLowerCase();
+
+                            int c = 0;
+                            int k = 0;
+                            if (value1.matches("[^0-9]") || value2.matches("[^0-9]")) {
+                                c = value1.compareTo(value2);
+                            } else {
+                                boolean numeric = false;
+                                while (k < value1.length() && k < value2.length()) {
+                                    while (k < value1.length() && k < value2.length() && Character.isDigit(value1.charAt(
+                                            k)) && Character.isDigit(value2.charAt(k))) {
+                                        int num1 = Character.getNumericValue(value1.charAt(k));
+                                        int num2 = Character.getNumericValue(value2.charAt(k));
+
+                                        if (c == 0) {
+                                            if (num1 < num2) {
+                                                c = -1;
+                                            }
+
+                                            if (num1 > num2) {
+                                                c = 1;
+                                            }
+
+                                        }
+                                        k++;
+                                        numeric =
+                                                true;
+                                    }
+
+                                    if (numeric && k < value1.length() && Character.isDigit(value1.charAt(
+                                            k))) {
+                                        c = 1;
+                                        numeric =
+                                                false;
+                                    } else if (numeric && k < value2.length() && Character.isDigit(value2.charAt(
+                                            k))) {
+                                        c = -1;
+                                        numeric =
+                                                false;
+                                    }
+
+                                    if (k < value1.length() && k < value2.length() && c == 0) {
+                                        c = Character.valueOf(value1.charAt(k)).compareTo(value2.charAt(
+                                                k));
+                                    }
+
+                                    if (c != 0) {
+                                        break;
+                                    }
+
+                                    k++;
+                                }
+
+                            }
+
+                            if (c == 0 && k < value1.length()) {
+                                c = 1;
+                            }
+
+                            if (c == 0 && k < value2.length()) {
+                                c = -1;
+                            }
+
+                            if (c != 0) {
+                                return (ascending[i] ? c : -c);
+                            }
+
+                        }
+                    }
+
+                    // All compares are equal
+                    return 0;
+                }
+            };
+
+            Collections.sort(tracks, comparator);
+        }
+
+    }
+
+
+    public void sortGroup(final RegionOfInterest region,
+                          String linkingAtt,
+                          final RegionScoreType type) {
+        List<Track> tracksWithScore = new ArrayList(getTracks().size());
+        List<Track> otherTracks = new ArrayList(getTracks().size());
+        for (Track t : getTracks()) {
+            if (t instanceof DataTrack) {
+                if (((DataTrack) t).isRegionScoreType(type)) {
+                    tracksWithScore.add(t);
+                } else {
+                    otherTracks.add(t);
+                }
+
+            } else {
+                otherTracks.add(t);
+            }
+
+        }
+
+        sortByRegionScore(tracksWithScore, region, type);
+        List<String> sortedAttributes = new ArrayList();
+        for (Track t : tracksWithScore) {
+            String att = t.getAttributeValue(linkingAtt);
+            if (att != null) {
+                sortedAttributes.add(att);
+            }
+
+        }
+        sortByAttributeOrder(otherTracks, sortedAttributes, linkingAtt);
+
+        clear();
+        addAll(tracksWithScore);
+        addAll(otherTracks);
+    }
+
+    public void sortByRegionScore(final RegionOfInterest region,
+                                  final RegionScoreType type) {
+        sortByRegionScore(tracks, region, type);
+    }
+
+
+    private void sortByRegionScore(List<Track> tracks,
+                                   final RegionOfInterest region,
+                                   final RegionScoreType type) {
+        if ((tracks != null) && (region != null) && !tracks.isEmpty()) {
+            final int zoom = Math.max(0, ViewContext.getInstance().getZoom());
+            final String chr = region.getChr();
+            final int start = region.getStart();
+            final int end = region.getEnd();
+
+            Comparator<Track> c = new Comparator<Track>() {
+
+                public int compare(Track t1, Track t2) {
+                    float s1 = t1.getRegionScore(chr, start, end, zoom, type);
+                    float s2 = t2.getRegionScore(chr, start, end, zoom, type);
+
+                    if (s2 > s1) {
+                        return 1;
+                    } else if (s1 < s2) {
+                        return -1;
+                    } else {
+                        return 0;
+                    }
+
+                }
+            };
+            Collections.sort(tracks, c);
+
+        }
+    }
+
+    /**
+     * @param sortedAttributes
+     * @param attributeId
+     */
+    private void sortByAttributeOrder(List<Track> tracks,
+                                      List<String> sortedAttributes,
+                                      final String attributeId) {
+        if ((tracks != null) && (sortedAttributes != null) && !tracks.isEmpty()) {
+
+            // Create a rank hash.  Loop backwards so that the lowest index for an attribute
+            final HashMap<String, Integer> rankMap = new HashMap(
+                    sortedAttributes.size() * 2);
+            for (int i = sortedAttributes.size() - 1; i >=
+                    0; i--) {
+                rankMap.put(sortedAttributes.get(i), i);
+            }
+            // Comparator for sorting in ascending order
+            Comparator<Track> c = new Comparator<Track>() {
+
+                public int compare(Track t1, Track t2) {
+                    String a1 = t1.getAttributeValue(attributeId);
+                    String a2 = t2.getAttributeValue(attributeId);
+                    Integer r1 = ((a1 == null) ? null : rankMap.get(a1));
+                    Integer r2 = ((a2 == null) ? null : rankMap.get(a2));
+                    if ((r1 == null) && (r2 == null)) {
+                        return 0;
+                    } else if (r1 == null) {
+                        return 1;
+                    } else if (r2 == null) {
+                        return -1;
+                    } else {
+                        return r1.intValue() - r2.intValue();
+                    }
+
+                }
+            };
+
+            Collections.sort(tracks, c);
+
+        }
+
+    }
+
+    /**
+     * @param trackIds
+     */
+    public void sortByList(List<String> trackIds) {
+
+        final Map<String, Integer> trackPositions = new HashMap();
+        for (int i = 0; i < trackIds.size(); i++) {
+            trackPositions.put(trackIds.get(i), i);
+        }
+        Comparator c = new Comparator<Track>() {
+            public int compare(Track t1, Track t2) {
+                String id1 = t1.getId();
+                int p1 = trackPositions.containsKey(id1) ? trackPositions.get(id1) : Integer.MAX_VALUE;
+                String id2 = t2.getId();
+                int p2 = trackPositions.containsKey(id2) ? trackPositions.get(id2) : Integer.MAX_VALUE;
+                return p1 - p2;
+            }
+        };
+        Collections.sort(tracks, c);
+    }
+
+    public void removeTracks(Collection<Track> tracksToRemove) {
+        tracks.removeAll(tracksToRemove);
+    }
+
+
+    /**
+     * If this group contains the targetTrack, insert the selectedTracks collection either before or after
+     * the target and return true.   Otherwise return false.
+     *
+     * @param selectedTracks
+     * @param targetTrack
+     * @param before
+     */
+    public boolean moveSelectedTracksTo(Collection<Track> selectedTracks,
+                                        Track targetTrack,
+                                        boolean before) {
+
+        int index = (targetTrack == null ? tracks.size() : tracks.indexOf(targetTrack));
+        if (index < 0) {
+            return false;
+        }
+
+        if (!before) {
+            index = index + 1;
+        }
+
+        // 1. Divdide the target list up into 2 parts, one before the index and one after
+        List<Track> beforeList = new ArrayList(tracks.subList(0, index));
+        List<Track> afterList = new ArrayList(tracks.subList(index, tracks.size()));
+
+        // 2.  Remove the selected tracks from anywhere they occur
+        beforeList.removeAll(selectedTracks);
+        afterList.removeAll(selectedTracks);
+
+        // 3. Now insert the selected tracks
+        tracks.clear();
+        tracks.addAll(beforeList);
+        tracks.addAll(selectedTracks);
+        tracks.addAll(afterList);
+
+        return true;
+
+    }
+
+    public boolean isSelected() {
+        return selected;
+    }
+
+    public void setSelected(boolean selected) {
+        this.selected = selected;
+        TrackManager tm = IGVMainFrame.getInstance().getTrackManager();
+        tm.clearSelections();
+        tm.setTrackSelections(new HashSet(tracks));
+
+    }
 }
diff --git a/src/org/broad/igv/track/TrackLoader.java b/src/org/broad/igv/track/TrackLoader.java
new file mode 100644
index 0000000..6ba3b7c
--- /dev/null
+++ b/src/org/broad/igv/track/TrackLoader.java
@@ -0,0 +1,939 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track;
+
+import org.apache.log4j.Logger;
+import org.broad.igv.PreferenceManager;
+import org.broad.igv.das.DASFeatureSource;
+import org.broad.igv.data.*;
+import org.broad.igv.data.rnai.RNAIDataSource;
+import org.broad.igv.data.rnai.RNAIGCTDatasetParser;
+import org.broad.igv.data.rnai.RNAIGeneScoreParser;
+import org.broad.igv.data.rnai.RNAIHairpinParser;
+import org.broad.igv.data.seg.SegmentedAsciiDataSet;
+import org.broad.igv.data.seg.SegmentedBinaryDataSet;
+import org.broad.igv.data.seg.SegmentedDataSource;
+import org.broad.igv.exceptions.DataLoadException;
+import org.broad.igv.feature.*;
+import org.broad.igv.feature.dranger.DRangerParser;
+import org.broad.igv.maf.MAFTrack;
+import org.broad.igv.maf.conservation.OmegaDataSource;
+import org.broad.igv.maf.conservation.OmegaTrack;
+import org.broad.igv.track.tribble.FeatureFileHeader;
+import org.broad.igv.renderer.*;
+import org.broad.igv.sam.*;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.synteny.BlastMapping;
+import org.broad.igv.synteny.BlastParser;
+import org.broad.igv.tdf.TDFDataSource;
+import org.broad.igv.tdf.TDFReader;
+import org.broad.igv.track.tribble.TribbleFeatureSource;
+import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.util.ConfirmDialog;
+import org.broad.igv.ui.util.MessageUtils;
+import org.broad.igv.util.IGVHttpUtils;
+import org.broad.igv.util.ParsingUtils;
+import org.broad.igv.util.ResourceLocator;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * User: jrobinso
+ * Date: Feb 14, 2010
+ */
+public class TrackLoader {
+
+    private static Logger log = Logger.getLogger(TrackLoader.class);
+
+    public List<Track> load(ResourceLocator locator) {
+
+        try {
+            String typeString = locator.getType();
+            if (typeString == null) {
+                typeString = locator.getPath().toLowerCase();
+                if (!typeString.endsWith("_sorted.txt") &&
+                        (typeString.endsWith(".txt") || typeString.endsWith(
+                                ".xls") || typeString.endsWith(".gz"))) {
+                    typeString = typeString.substring(0, typeString.lastIndexOf("."));
+                }
+            }
+            typeString = typeString.toLowerCase();
+
+
+            if (typeString.endsWith(".tbi")) {
+                MessageUtils.showMessage("<html><b>Error:</b>File type '.tbi' is not recognized.  If this is a 'tabix' index <br>" +
+                        " load the associated gzipped file, which should have an extension of '.gz'");
+            }
+
+            // Check for index
+            boolean hasIndex = false;
+            if (locator.isLocal()) {
+                File indexFile = new File(locator.getPath() + ".sai");
+                hasIndex = indexFile.exists();
+            }
+
+            List<Track> newTracks = new ArrayList<Track>();
+
+            if (typeString.endsWith("das")) {
+                loadDASResource(locator, newTracks);
+            } else if (isIndexed(locator.getPath())) {
+                loadIndexed(locator, newTracks);
+
+
+            } else if (typeString.endsWith("vcf")) {
+                // TODO This is a hack,  vcf files must be indexed.  Fix in next release.
+                throw new DataLoadException("VCF files must be indexed.  Use igvtools or tabix to index the file prior to loading.",
+                        locator.getPath());
+            } else if (typeString.endsWith("h5") || typeString.endsWith("hbin")) {
+                loadH5File(locator, newTracks);
+            } else if (typeString.endsWith("rnai.gct")) {
+                loadRnaiGctFile(locator, newTracks);
+            } else if (typeString.endsWith("gct") || typeString.endsWith("res") || typeString.endsWith("tab")) {
+                loadGctFile(locator, newTracks);
+            } else if (typeString.endsWith("cn") || typeString.endsWith("xcn") || typeString.endsWith("snp") ||
+                    typeString.endsWith("igv") || typeString.endsWith("loh")) {
+                loadIGVFile(locator, newTracks);
+            } else if (typeString.endsWith("mut")) {
+                loadMutFile(locator, newTracks);
+            } else if (typeString.endsWith("cbs") || typeString.endsWith("seg") || typeString.endsWith(".gbench") ||
+                    typeString.endsWith("glad") || typeString.endsWith("birdseye_canary_calls")) {
+                loadSegFile(locator, newTracks);
+            } else if (typeString.endsWith("seg.zip")) {
+                loadBinarySegFile(locator, newTracks);
+            } else if (typeString.endsWith("gistic")) {
+                loadGisticFile(locator, newTracks);
+            } else if (typeString.endsWith(".gs")) {
+                loadRNAiGeneScoreFile(locator, newTracks, RNAIGeneScoreParser.Type.GENE_SCORE);
+            } else if (typeString.endsWith("riger")) {
+                loadRNAiGeneScoreFile(locator, newTracks, RNAIGeneScoreParser.Type.POOLED);
+            } else if (typeString.endsWith(".hp")) {
+                loadRNAiHPScoreFile(locator);
+            } else if (typeString.endsWith("gene")) {
+                loadGeneFile(locator, newTracks);
+            } else if (typeString.contains("tabblastn") || typeString.endsWith("orthologs")) {
+                loadSyntentyMapping(locator, newTracks);
+            } else if (typeString.endsWith(".sam") || typeString.endsWith(".bam") ||
+                    typeString.endsWith(".sam.list") || typeString.endsWith(".bam.list") ||
+                    typeString.endsWith("_sorted.txt") ||
+                    typeString.endsWith(".aligned") || typeString.endsWith(".sai") ||
+                    typeString.endsWith(".bai")) {
+                loadAlignmentsTrack(locator, newTracks);
+            } else if (typeString.endsWith(".bedz") || (typeString.endsWith(".bed") && hasIndex)) {
+                loadIndexdBedFile(locator, newTracks);
+            } else if (typeString.endsWith("omega")) {
+                loadOmegaTrack(locator, newTracks);
+            } else if (typeString.endsWith("wig") || (typeString.endsWith("bedgraph"))) {
+                loadWigFile(locator, newTracks);
+            } else if (typeString.endsWith("list")) {
+                loadListFile(locator, newTracks);
+            } else if (typeString.contains("dranger")) {
+                loadDRangerFile(locator, newTracks);
+            } else if (typeString.endsWith("ewig.tdf") || (typeString.endsWith("ewig.ibf"))) {
+                loadEwigIBFFile(locator, newTracks);
+            } else if (typeString.endsWith(".ibf") || typeString.endsWith(".tdf")) {
+                loadTDFFile(locator, newTracks);
+            } else if (GCTDatasetParser.parsableMAGE_TAB(locator)) {
+                locator.setDescription("MAGE_TAB");
+                loadGctFile(locator, newTracks);
+            } else if (IGVDatasetParser.parsableMAGE_TAB(locator)) {
+                locator.setDescription("MAGE_TAB");
+                loadIGVFile(locator, newTracks);
+            } else if (typeString.endsWith(".psl") || typeString.endsWith(".psl.gz") ||
+                    typeString.endsWith(".pslx") || typeString.endsWith(".pslx.gz")) {
+                loadPslFile(locator, newTracks);
+            } else if (AbstractFeatureParser.getInstanceFor(locator) != null) {
+                loadFeatureFile(locator, newTracks);
+            } else if (MutationParser.isMutationAnnotationFile(locator)) {
+                this.loadMutFile(locator, newTracks);
+            } else if (WiggleParser.isWiggle(locator)) {
+                loadWigFile(locator, newTracks);
+            } else if (locator.getPath().toLowerCase().contains(".maf")) {
+                loadMAFTrack(locator, newTracks);
+            } else {
+                AttributeManager.getInstance().loadSampleInfo(locator);
+            }
+
+            // Track line
+            TrackProperties tp = null;
+            String trackLine = locator.getTrackLine();
+            if (trackLine != null) {
+                tp = new TrackProperties();
+                ParsingUtils.parseTrackLine(trackLine, tp);
+            }
+
+
+            for (Track track : newTracks) {
+                track.setSourceFile(locator.getPath());
+                if (locator.getUrl() != null) {
+                    track.setUrl(locator.getUrl());
+                }
+                if (tp != null) {
+                    track.setTrackProperties(tp);
+                }
+                if (locator.getColor() != null) {
+                    track.setColor(locator.getColor());
+                }
+                if (locator.getSampleId() != null) {
+                    track.setSampleId(locator.getSampleId());
+                }
+                IGVMainFrame.getInstance().getTrackManager().addLoadedType(track.getTrackType());
+            }
+
+
+            return newTracks;
+
+        } catch (DataLoadException dle) {
+            throw dle;
+        } catch (Exception e) {
+            log.error(e);
+            throw new DataLoadException(e.getMessage(), locator.getPath());
+        }
+
+    }
+
+    private void loadIndexed(ResourceLocator locator, List<Track> newTracks) throws IOException {
+
+        TribbleFeatureSource src = new TribbleFeatureSource(locator.getPath());
+        FeatureFileHeader header = (FeatureFileHeader) src.getHeader();
+        Track t = new FeatureTrack(locator, src);
+
+        t.setName(locator.getTrackName());
+        if (header != null) {
+            if (header.getTrackType() != null) {
+                t.setTrackType(header.getTrackType());
+            }
+            if (header.getTrackProperties() != null) {
+                t.setTrackProperties(header.getTrackProperties());
+            }
+
+            if (header.getTrackType() == TrackType.REPMASK) {
+                t.setHeight(15);
+            }
+        }
+
+        newTracks.add(t);
+    }
+
+    /**
+     * Load the input file as a BED or Attribute (Sample Info) file.  First assume
+     * it is a BED file,  if no features are found load as an attribute file.
+     *
+     * @param locator
+     * @param newTracks
+     */
+    private void loadGeneFile(ResourceLocator locator, List<Track> newTracks) {
+
+        FeatureParser featureParser = AbstractFeatureParser.getInstanceFor(locator);
+        if (featureParser != null) {
+            List<FeatureTrack> tracks = featureParser.loadTracks(locator);
+            newTracks.addAll(tracks);
+        }
+
+    }
+
+    private void loadSyntentyMapping(ResourceLocator locator, List<Track> newTracks) {
+        List<BlastMapping> mappings = (new BlastParser()).parse(locator.getPath());
+        List<Feature> features = new ArrayList<Feature>(mappings.size());
+        features.addAll(mappings);
+
+        Track track = new FeatureTrack(locator, new FeatureCollectionSource(features));
+        track.setName(locator.getTrackName());
+        // track.setRendererClass(AlignmentBlockRenderer.class);
+        newTracks.add(track);
+    }
+
+    private void loadDRangerFile(ResourceLocator locator, List<Track> newTracks) {
+
+        DRangerParser parser = new DRangerParser();
+        newTracks.addAll(parser.loadTracks(locator));
+    }
+
+    /**
+     * Load the input file as a feature, muation, or maf (multiple alignment) file.
+     *
+     * @param locator
+     * @param newTracks
+     */
+    private void loadPslFile(ResourceLocator locator, List<Track> newTracks) throws IOException {
+
+        PSLParser featureParser = new PSLParser();
+        List<FeatureTrack> tracks = featureParser.loadTracks(locator);
+        newTracks.addAll(tracks);
+        for (Track t : tracks) {
+            ((FeatureTrack) t).setMinimumHeight(10);
+            t.setHeight(30);
+            t.setExpanded(true);
+        }
+
+
+    }
+
+    /**
+     * Load the input file as a feature, muation, or maf (multiple alignment) file.
+     *
+     * @param locator
+     * @param newTracks
+     */
+    private void loadFeatureFile(ResourceLocator locator, List<Track> newTracks) throws IOException {
+
+        if (locator.isLocal() && (locator.getPath().endsWith(".bed") ||
+                locator.getPath().endsWith(".bed.txt") ||
+                locator.getPath().endsWith(".bed.gz"))) {
+            if (!checkSize(locator.getPath())) {
+                return;
+            }
+        }
+
+        FeatureParser featureParser = AbstractFeatureParser.getInstanceFor(locator);
+        if (featureParser != null) {
+            List<FeatureTrack> tracks = featureParser.loadTracks(locator);
+            newTracks.addAll(tracks);
+        } else if (MutationParser.isMutationAnnotationFile(locator)) {
+            this.loadMutFile(locator, newTracks);
+        } else if (WiggleParser.isWiggle(locator)) {
+            loadWigFile(locator, newTracks);
+        } else if (locator.getPath().toLowerCase().contains(".maf")) {
+            loadMAFTrack(locator, newTracks);
+        }
+
+
+    }
+
+    /**
+     * Load the input file as a feature, muation, or maf (multiple alignment) file.
+     *
+     * @param locator
+     * @param newTracks
+     */
+    private void loadIndexdBedFile(ResourceLocator locator, List<Track> newTracks) throws IOException {
+
+        File featureFile = new File(locator.getPath());
+        File indexFile = new File(locator.getPath() + ".sai");
+        FeatureSource src = new IndexedBEDFeatureSource(featureFile, indexFile);
+        Track t = new FeatureTrack(locator, src);
+        newTracks.add(t);
+
+    }
+
+    private void loadRnaiGctFile(ResourceLocator locator, List<Track> newTracks) {
+
+        RNAIGCTDatasetParser parser = new RNAIGCTDatasetParser(locator);
+
+        Collection<RNAIDataSource> dataSources = parser.parse();
+        if (dataSources != null) {
+            String path = locator.getPath();
+            for (RNAIDataSource ds : dataSources) {
+                String trackId = path + "_" + ds.getName();
+                Track track = new DataSourceTrack(locator, trackId, ds.getName(), ds);
+
+                // Set attributes.
+                track.setAttributeValue("SCREEN", ds.getScreen());
+                track.setHeight(80);
+                newTracks.add(track);
+            }
+        }
+    }
+
+    private void loadGctFile(ResourceLocator locator, List<Track> newTracks) {
+
+        if (locator.isLocal()) {
+            if (!checkSize(locator.getPath())) {
+                return;
+            }
+        }
+
+        GCTDatasetParser parser = null;
+        GCTDataset ds = null;
+
+        String fName = locator.getTrackName();
+
+        // TODO -- handle remote resource
+        try {
+            parser = new GCTDatasetParser(locator, null,
+                    ViewContext.getInstance().getGenomeId());
+        } catch (IOException e) {
+            log.error("Error creating GCT parser.", e);
+            throw new DataLoadException("Error creating GCT parser: " + e, locator.getPath());
+        }
+        ds = parser.createDataset();
+        ds.setName(fName);
+        ds.setNormalized(true);
+        ds.setLogValues(true);
+
+        /*
+         * File outputFile = new File(IGVMainFrame.DEFAULT_USER_DIRECTORY, file.getName() + ".h5");
+         * OverlappingProcessor proc = new OverlappingProcessor(ds);
+         * proc.setZoomMax(0);
+         * proc.process(outputFile.getAbsolutePath());
+         * loadH5File(outputFile, messages, attributeList, group);
+         */
+
+        // Counter for generating ID
+        TrackProperties trackProperties = ds.getTrackProperties();
+        String path = locator.getPath();
+        for (String trackName : ds.getDataHeadings()) {
+            Genome currentGenome = ViewContext.getInstance().getGenome();
+            DatasetDataSource dataSource = new DatasetDataSource(currentGenome, trackName, ds);
+            String trackId = path + "_" + trackName;
+            Track track = new DataSourceTrack(locator, trackId, trackName, dataSource);
+            track.setRendererClass(HeatmapRenderer.class);
+            track.setTrackProperties(trackProperties);
+            newTracks.add(track);
+        }
+    }
+
+    private void loadIGVFile(ResourceLocator locator, List<Track> newTracks) {
+
+        if (locator.isLocal()) {
+            if (!checkSize(locator.getPath())) {
+                return;
+            }
+        }
+
+
+        String dsName = locator.getTrackName();
+        String currentGenomeId = ViewContext.getInstance().getGenomeId();
+
+        IGVDataset ds = new IGVDataset(currentGenomeId, locator);
+        ds.setName(dsName);
+
+        TrackProperties trackProperties = ds.getTrackProperties();
+        String path = locator.getPath();
+        TrackType type = ds.getType();
+        for (String trackName : ds.getDataHeadings()) {
+
+            Genome currentGenome = ViewContext.getInstance().getGenome();
+            DatasetDataSource dataSource = new DatasetDataSource(currentGenome, trackName, ds);
+            String trackId = path + "_" + trackName;
+            DataSourceTrack track = new DataSourceTrack(locator, trackId, trackName, dataSource);
+
+            // track.setRendererClass(HeatmapRenderer.class);
+            track.setTrackType(ds.getType());
+            track.setTrackProperties(trackProperties);
+
+            if (type == TrackType.ALLELE_FREQUENCY) {
+                track.setRendererClass(ScatterplotRenderer.class);
+                track.setHeight(40);
+            }
+            newTracks.add(track);
+        }
+    }
+
+    private boolean checkSize(String file) {
+
+        if (!PreferenceManager.getInstance().getBooleanPreference(PreferenceManager.SHOW_SIZE_WARNING, true)) {
+            return true;
+        }
+
+        File f = new File(file);
+        String tmp = file;
+        if (f.exists()) {
+            long size = f.length();
+            if (file.endsWith(".gz")) {
+                size *= 3;
+                tmp = file.substring(0, file.length() - 3);
+            }
+
+            if (size > 50000000) {
+                String message = "";
+                if (tmp.endsWith(".bed") || tmp.endsWith(".bed.txt")) {
+                    message = "The file " + file + " is large (" + (size / 1000000) + " mb).  It is recommended " +
+                            "that large files be indexed using IGVTools or Tabix. Loading un-indexed " +
+                            "ascii fies of this size can lead to poor performance or unresponsiveness (freezing).  " +
+                            "<br><br>IGVTools can be launched from the <b>Tools</b> menu or separately as a command line program.  " +
+                            "See the user guide for more details.<br><br>Click <b>Continue</b> to continue loading, or <b>Cancel</b>" +
+                            " to skip this file.";
+
+                } else {
+
+                    message = "The file " + file + " is large (" + (size / 1000000) + " mb).  It is recommended " +
+                            "that large files be converted to the binary <i>.tdf</i> format using the IGVTools " +
+                            "<b>tile</b> command. Loading  unconverted ascii fies of this size can lead to poor " +
+                            "performance or unresponsiveness (freezing).  " +
+                            "<br><br>IGVTools can be launched from the <b>Tools</b> menu or separately as a " +
+                            "command line program. See the user guide for more details.<br><br>Click <b>Continue</b> " +
+                            "to continue loading, or <b>Cancel</b> to skip this file.";
+                }
+
+                return ConfirmDialog.optionallyShowInfoDialog(message, PreferenceManager.SHOW_SIZE_WARNING, true);
+
+            }
+        }
+        return true;
+    }
+
+    private void loadDOTFile(ResourceLocator locator, List<Track> newTracks) {
+
+        //GraphTrack gt = new GraphTrack(locator);
+        //gt.setHeight(80);
+        //newTracks.add(gt);
+
+    }
+
+    private void loadWigFile(ResourceLocator locator, List<Track> newTracks) {
+
+        if (locator.isLocal()) {
+            if (!checkSize(locator.getPath())) {
+                return;
+            }
+        }
+
+        String genome = ViewContext.getInstance().getGenomeId();
+
+        WiggleDataset ds = (new WiggleParser(locator, genome)).parse();
+        TrackProperties props = ds.getTrackProperties();
+
+        // In case of conflict between the resource locator display name and the track properties name,
+        // use the resource locator
+        String name = props == null ? null : props.getName();
+        String label = locator.getName();
+        if (name == null) {
+            name = locator.getFileName();
+        } else if (label != null) {
+            props.setName(label);  // erase name rom track properties
+        }
+
+        String path = locator.getPath();
+        boolean multiTrack = ds.getDataHeadings().length > 1;
+
+        for (String heading : ds.getDataHeadings()) {
+
+            String trackId = multiTrack ? path + "_" + heading : path;
+            String trackName = multiTrack ? heading : name;
+
+            Genome currentGenome = ViewContext.getInstance().getGenome();
+            DatasetDataSource dataSource = new DatasetDataSource(currentGenome, trackId, ds);
+
+            Track track = new DataSourceTrack(locator, trackId, trackName, dataSource);
+
+            String displayName = (label == null || multiTrack) ? heading : label;
+            track.setName(displayName);
+            track.setTrackProperties(props);
+
+            track.setTrackType(ds.getType());
+            newTracks.add(track);
+        }
+    }
+
+    private void loadTDFFile(ResourceLocator locator, List<Track> newTracks) {
+
+
+        if (log.isDebugEnabled()) {
+            log.debug("Loading TDFFile: " + locator.toString());
+        }
+
+        TDFReader reader = TDFReader.getReader(locator.getPath());
+        TrackType type = reader.getTrackType();
+
+        if (log.isDebugEnabled()) {
+            log.debug("Parsing track line ");
+        }
+        TrackProperties props = null;
+        String trackLine = reader.getTrackLine();
+        if (trackLine != null && trackLine.length() > 0) {
+            props = new TrackProperties();
+            ParsingUtils.parseTrackLine(trackLine, props);
+        }
+
+        // In case of conflict between the resource locator display name and the track properties name,
+        // use the resource locator
+        String name = locator.getName();
+        if (name != null && props != null) {
+            props.setName(name);
+        }
+
+        if (name == null) {
+            name = props == null ? locator.getTrackName() : props.getName();
+        }
+
+        int trackNumber = 0;
+        String path = locator.getPath();
+        boolean multiTrack = reader.getTrackNames().length > 1;
+
+        for (String heading : reader.getTrackNames()) {
+
+            String trackId = multiTrack ? path + "_" + heading : path;
+            String trackName = multiTrack ? heading : name;
+            String id142 = multiTrack ? trackId : heading;
+
+            DataSourceTrack track = new DataSourceTrack(locator, trackId, trackName, new TDFDataSource(reader, trackNumber, heading));
+            track.setId_142(id142);
+            String displayName = (name == null || multiTrack) ? heading : name;
+            track.setName(displayName);
+            track.setTrackType(type);
+            if (props != null) {
+                track.setTrackProperties(props);
+            }
+            newTracks.add(track);
+            trackNumber++;
+        }
+    }
+
+    private void loadEwigIBFFile(ResourceLocator locator, List<Track> newTracks) {
+
+        TDFReader reader = TDFReader.getReader(locator.getPath());
+        TrackProperties props = null;
+        String trackLine = reader.getTrackLine();
+        if (trackLine != null && trackLine.length() > 0) {
+            props = new TrackProperties();
+            ParsingUtils.parseTrackLine(trackLine, props);
+        }
+
+        Track track = new EWigTrack(locator);
+        if (props != null) {
+            track.setTrackProperties(props);
+        }
+        track.setName(locator.getTrackName());
+        newTracks.add(track);
+    }
+
+    private void loadListFile(ResourceLocator locator, List<Track> newTracks) {
+        try {
+            FeatureSource source = new FeatureDirSource(locator);
+            FeatureTrack track = new FeatureTrack(locator, source);
+            track.setName(locator.getTrackName());
+            track.setVisibilityWindow(100000);
+            newTracks.add(track);
+        } catch (IOException ex) {
+            throw new RuntimeException(ex);
+        }
+
+    }
+
+    private void loadGisticFile(ResourceLocator locator, List<Track> newTracks) {
+
+        GisticTrack track = GisticFileParser.loadData(locator);
+        track.setName(locator.getTrackName());
+        newTracks.add(track);
+    }
+
+    /**
+     * Load the data from an HDF5 file and add resulting tracks to the supplied TrackGroup.
+     * Error messages are appended to the MessageCollection
+     *
+     * @param locator
+     */
+    private void loadH5File(ResourceLocator locator, List<Track> newTracks) {
+
+        TrackSet trackSet = null;
+
+        // TODO -- temporary until "name" property is added to locator
+        // TODO -- "label" has been added, how does that affect this?
+
+        String fName = locator.getTrackName();
+
+        HDFDataManager dataManager = HDFDataManagerFactory.getDataManager(locator);
+        TrackProperties trackProperties = dataManager.getTrackProperties();
+        String[] trackNames = dataManager.getTrackNames();
+
+        List<Track> tracks = new ArrayList();
+
+        for (int trackNumber = 0; trackNumber < trackNames.length; trackNumber++) {
+            String name = trackNames.length == 1 ? fName : trackNames[trackNumber];
+
+            Track track = null;
+            try {
+                track = new HDFDataTrack(dataManager, locator, name, trackNumber);
+            } catch (FileNotFoundException fe) {
+                throw new RuntimeException(fe);
+            }
+            if (trackProperties != null) {
+                track.setTrackProperties(trackProperties);
+            }
+
+            tracks.add(track);
+        }
+
+        trackSet = new TrackSet(tracks);
+
+        if (trackSet.isEmpty()) {
+            throw new RuntimeException("No data found in file");
+        }
+
+        newTracks.addAll(trackSet.getTracks());
+    }
+
+    /**
+     * Load a rnai gene score file and create a datasource and track.
+     * <p/>
+     * // TODO -- change parser to use resource locator rather than path.
+     *
+     * @param locator
+     * @param newTracks
+     */
+    private void loadRNAiGeneScoreFile(ResourceLocator locator,
+                                       List<Track> newTracks, RNAIGeneScoreParser.Type type) {
+
+        String genomeId = ViewContext.getInstance().getGenomeId();
+        RNAIGeneScoreParser parser = new RNAIGeneScoreParser(locator.getPath(), genomeId, type);
+
+        Collection<RNAIDataSource> dataSources = parser.parse();
+        String path = locator.getPath();
+        for (RNAIDataSource ds : dataSources) {
+            String name = ds.getName();
+            String trackId = path + "_" + name;
+            Track track = new DataSourceTrack(locator, trackId, name, ds);
+
+            // Set attributes.  This "hack" is neccessary to register these attributes with the
+            // attribute manager to get displayed.
+            track.setAttributeValue("SCREEN", ds.getScreen());
+            if ((ds.getCondition() != null) && (ds.getCondition().length() > 0)) {
+                track.setAttributeValue("CONDITION", ds.getCondition());
+            }
+            track.setHeight(80);
+            //track.setDataRange(new DataRange(-3, 0, 3));
+            newTracks.add(track);
+        }
+
+    }
+
+    /**
+     * Load a RNAi haripin score file.  The results of this action are hairpin scores
+     * added to the RNAIDataManager.  Currently no tracks are created for hairpin
+     * scores, although this could change.
+     * <p/>
+     * // TODO -- change parser to use resource locator rather than path.
+     *
+     * @param locator
+     */
+    private void loadRNAiHPScoreFile(ResourceLocator locator) {
+        (new RNAIHairpinParser(locator.getPath())).parse();
+    }
+
+    private void loadMAFTrack(ResourceLocator locator, List<Track> newTracks) {
+        MAFTrack t = new MAFTrack(locator);
+        t.setName("Multiple Alignments");
+        newTracks.add(t);
+    }
+
+    private void loadOmegaTrack(ResourceLocator locator, List<Track> newTracks) {
+        OmegaDataSource ds = new OmegaDataSource();
+        Track track = new OmegaTrack(locator, ds);
+        track.setName("Conservation (Omega)");
+        track.setHeight(40);
+        newTracks.add(track);
+    }
+
+    /**
+     * Load a rnai gene score file and create a datasource and track.
+     * <p/>
+     * // TODO -- change parser to use resource locator rather than path.
+     *
+     * @param locator
+     * @param newTracks
+     */
+    private void loadAlignmentsTrack(ResourceLocator locator, List<Track> newTracks) {
+
+        try {
+            String dsName = locator.getTrackName();
+            String fn = locator.getPath().toLowerCase();
+            boolean isBed = fn.endsWith(".bedz") || fn.endsWith(".bed") || fn.endsWith(".bed.gz");
+
+            // If the user tried to load the index,  look for the file (this is a common mistake)
+            if (locator.getPath().endsWith(".sai") || locator.getPath().endsWith(".bai")) {
+                MessageUtils.showMessage("<html><b>ERROR:</b> Loading SAM/BAM index files are not supported:  " + locator.getPath() +
+                        "<br>Load the SAM or BAM file directly. ");
+                return;
+            }
+            AlignmentDataManager dataManager = AlignmentDataManager.getDataManager(locator);
+
+            if (locator.getPath().toLowerCase().endsWith(".bam")) {
+                if (!dataManager.hasIndex()) {
+                    MessageUtils.showMessage("<html>Could not load index file for: " +
+                            locator.getPath() + "<br>  An index file is required for SAM & BAM files.");
+                    return;
+                }
+            }
+
+            AlignmentTrack track = new AlignmentTrack(locator, dataManager);    // parser.loadTrack(locator, dsName);
+            track.setName(dsName);
+            if (isBed) {
+                track.setRenderer(new BedRenderer());
+                track.setHeight(40);
+            }
+
+            // Create coverage track
+            CoverageTrack covTrack = new CoverageTrack(locator.getPath() + "_coverage", track.getName() + " Coverage");
+            covTrack.setVisible(PreferenceManager.getInstance().getSAMPreferences().isShowCoverageTrack());
+            newTracks.add(covTrack);
+            track.setCoverageTrack(covTrack);
+            if (!isBed) {
+                covTrack.setDataManager(dataManager);
+                dataManager.setCoverageTrack(covTrack);
+            }
+
+            // Track line
+            String trackLine = locator.getTrackLine();
+            if (trackLine != null) {
+                TrackProperties tp = new TrackProperties();
+                ParsingUtils.parseTrackLine(trackLine, tp);
+                covTrack.setTrackProperties(tp);
+            }
+
+            // Search for precalculated coverage data
+            String covPath = locator.getCoverage();
+            if (covPath == null) {
+                String path = locator.getPath();
+                covPath = path + ".tdf";
+            }
+            if (covPath != null) {
+                try {
+                    // TODO -- ftp
+                    if ((new File(covPath)).exists() ||
+                            ((covPath.startsWith("http:") || covPath.startsWith("https:")) &&
+                                    IGVHttpUtils.resourceAvailable(new URL(covPath)))) {
+                        TDFReader reader = TDFReader.getReader(covPath);
+                        TDFDataSource ds = new TDFDataSource(reader, 0, track.getName() + " coverage");
+                        covTrack.setDataSource(ds);
+                    }
+                } catch (MalformedURLException e) {
+                    // This is expected if
+                    //    log.info("Could not loading coverage data: MalformedURL: " + covPath);
+                }
+            }
+
+            newTracks.add(track);
+
+        } catch (Exception e) {
+            log.error("Error loading sam track", e);
+            throw new RuntimeException(e);
+        }
+    }
+
+
+    /**
+     * Load a ".mut" file (muation file) and create tracks.
+     *
+     * @param locator
+     * @param newTracks
+     */
+    private void loadMutFile(ResourceLocator locator, List<Track> newTracks) {
+
+        MutationParser parser = new MutationParser();
+        List<Track> mutationTracks = parser.loadMutationTracks(locator);
+        for (Track track : mutationTracks) {
+            track.setTrackType(TrackType.MUTATION);
+            track.setRendererClass(MutationRenderer.class);
+            newTracks.add(track);
+        }
+    }
+
+    private void loadSegFile(ResourceLocator locator, List<Track> newTracks) {
+
+        // TODO - -handle remote resource
+        SegmentedAsciiDataSet ds = new SegmentedAsciiDataSet(locator);
+        String path = locator.getPath();
+        TrackProperties props = ds.getTrackProperties();
+        for (String trackName : ds.getDataHeadings()) {
+            String trackId = path + "_" + trackName;
+            SegmentedDataSource dataSource = new SegmentedDataSource(trackName, ds);
+            DataSourceTrack track = new DataSourceTrack(locator, trackId, trackName, dataSource);
+            track.setId_142(trackName);
+            track.setRendererClass(HeatmapRenderer.class);
+            track.setTrackType(ds.getType());
+
+            if (props != null) {
+                track.setTrackProperties(props);
+            }
+
+            newTracks.add(track);
+        }
+    }
+
+    private void loadBinarySegFile(ResourceLocator locator, List<Track> newTracks) {
+
+        SegmentedBinaryDataSet ds = new SegmentedBinaryDataSet(locator);
+        String path = locator.getPath();
+        for (String trackName : ds.getSampleNames()) {
+            String trackId = path + "_" + trackName;
+            SegmentedDataSource dataSource = new SegmentedDataSource(trackName, ds);
+            Track track = new DataSourceTrack(locator, trackId, trackName, dataSource);
+            track.setRendererClass(HeatmapRenderer.class);
+            track.setTrackType(ds.getType());
+            newTracks.add(track);
+        }
+    }
+
+
+    private void loadDASResource(ResourceLocator locator, List<Track> currentTracks) {
+
+        //TODO Connect and get all the attributes of the DAS server, and run the appropriate load statements
+        //TODO Currently we are only going to be doing features
+        // TODO -- move the source creation to a factory
+
+
+        DASFeatureSource featureSource = null;
+        try {
+            featureSource = new DASFeatureSource(locator);
+        } catch (MalformedURLException e) {
+            log.error("Malformed URL", e);
+            throw new DataLoadException("Error: Malformed URL ", locator.getPath());
+        }
+
+        FeatureTrack track = new FeatureTrack(locator, featureSource);
+
+        // Try to create a sensible name from the path
+        String name = locator.getPath();
+        if (locator.getPath().contains("genome.ucsc.edu")) {
+            name = featureSource.getType();
+        } else {
+            name = featureSource.getPath().replace("/das/", "").replace("/features", "");
+        }
+        track.setName(name);
+
+        // A hack until we can notate this some other way
+        if (locator.getPath().contains("cosmic")) {
+            track.setRendererClass(CosmicFeatureRenderer.class);
+            track.setMinimumHeight(2);
+            track.setHeight(20);
+            track.setExpanded(true);
+        } else {
+            track.setRendererClass(BasicFeatureRenderer.class);
+            track.setMinimumHeight(35);
+            track.setHeight(45);
+        }
+        currentTracks.add(track);
+    }
+
+
+    public static boolean isIndexed(String path) {
+        String indexExtension = path.endsWith("gz") ? ".tbi" : ".idx";
+        String indexPath = path + indexExtension;
+        try {
+            // TODO -- ftp
+            if (path.startsWith("http:") || path.startsWith("https:")) {
+                return IGVHttpUtils.resourceAvailable(new URL(indexPath));
+            } else {
+                File f = new File(path + indexExtension);
+                return f.exists();
+            }
+
+        } catch (IOException e) {
+            return false;
+        }
+
+    }
+}
diff --git a/src/org/broad/igv/track/TrackManager.java b/src/org/broad/igv/track/TrackManager.java
index 852b91e..9ade623 100644
--- a/src/org/broad/igv/track/TrackManager.java
+++ b/src/org/broad/igv/track/TrackManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -19,265 +19,138 @@ package org.broad.igv.track;
 
 //~--- non-JDK imports --------------------------------------------------------
 
+import net.sf.samtools.util.BlockCompressedInputStream;
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
 import org.broad.igv.PreferenceManager;
-import org.broad.igv.das.DASConnection;
-import org.broad.igv.das.DASFeatureParser;
-import org.broad.igv.das.DASFileSource;
-import org.broad.igv.das.DASSource;
-import org.broad.igv.data.*;
-import org.broad.igv.data.rnai.RNAIDataSource;
-import org.broad.igv.data.rnai.RNAIGCTDatasetParser;
-import org.broad.igv.data.rnai.RNAIGeneScoreParser;
-import org.broad.igv.data.rnai.RNAIHairpinParser;
 import org.broad.igv.exceptions.DataLoadException;
-import org.broad.igv.feature.*;
-import org.broad.igv.feature.dranger.DRangerParser;
-import org.broad.igv.maf.MAFTrack;
-import org.broad.igv.maf.conservation.OmegaDataSource;
-import org.broad.igv.maf.conservation.OmegaTrack;
-import org.broad.igv.renderer.DASFeatureRenderer;
-import org.broad.igv.renderer.GeneTrackRenderer;
-import org.broad.igv.renderer.HeatmapRenderer;
-import org.broad.igv.renderer.MutationRenderer;
-import org.broad.igv.sam.*;
-import org.broad.igv.synteny.Mapping;
-import org.broad.igv.synteny.MappingParser;
-import org.broad.igv.tdf.TDFDataSource;
-import org.broad.igv.tdf.TDFReader;
+import org.broad.igv.feature.Feature;
+import org.broad.igv.feature.GeneManager;
+import org.broad.igv.feature.Genome;
+import org.broad.igv.feature.GenomeManager;
+import org.broad.igv.renderer.BasicFeatureRenderer;
+import org.broad.igv.sam.AlignmentTrack;
+import org.broad.igv.sam.CoverageTrack;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.ui.IGVMainFrame;
-import org.broad.igv.ui.IGVModel;
 import org.broad.igv.ui.MessageCollection;
 import org.broad.igv.ui.RegionOfInterest;
+import org.broad.igv.ui.UIConstants;
 import org.broad.igv.ui.panel.DragEventManager;
 import org.broad.igv.ui.panel.DragListener;
+import org.broad.igv.ui.panel.TrackPanel;
+import org.broad.igv.ui.panel.TrackPanelScrollPane;
 import org.broad.igv.ui.util.MessageUtils;
-import org.broad.igv.ui.util.UIUtilities;
-import org.broad.igv.util.HttpUtils;
-import org.broad.igv.util.ParsingUtils;
 import org.broad.igv.util.ResourceLocator;
 
 import java.awt.*;
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.net.MalformedURLException;
+import java.io.InputStream;
 import java.net.URL;
 import java.util.*;
 import java.util.List;
 
-/**
- *  Misc globally visible parameters.  These need a home.
- *
- */
 
 /**
- * Placeholder for miscelleneous stuff that hasn't found a home yet.
+ * A helper class to manage tracks and groups.
  */
+
 public class TrackManager {
 
-    public static final int GENE_TRACK_HEIGHT = 35;
-    public static final int GISTIC_TRACK_HEIGHT = 50;
     private static Logger log = Logger.getLogger(TrackManager.class);
-    private static TrackManager theInstance = null;
-    static List emptyList = new ArrayList();
-    final public static String GENE_TRACK_NAME = "Gene";
-    private String groupByAttribute = null;
-
-    private boolean restoringSession = false;
 
     /**
-     * The gene track.  Always present, rendered in the FeaturePanel
-     */
-    private Track geneTrack;
-    /**
-     * A map of data panel name -> track group
-     */
-    Map<String, TrackGroup> panelTrackGroups;
-    /**
-     * A map of data panel name -> [map of group name -> track group]
+     * Owning object
      */
-    Map<String, Map<String, TrackGroup>> trackGroups;
-    // Hack for overlay experiment
-    public static String OVERLAY_DATASET = "";
-    Map<String, List<Track>> overlayTracksMap = new HashMap();
-    /**
-     * Map of id -> track.
-     * <p/>
-     * Note: assumes ids are unique, but there is nothing that enforces this.
-     */
-    Map<String, Track> trackDictionary = new Hashtable();
+    IGVMainFrame mainFrame;
 
-    static public enum MatchTrack {
+    private TrackLoader loader;
 
-        EXACT, CONTAINS;
-    }
+    private final Map<String, TrackPanelScrollPane> trackPanelScrollPanes = new Hashtable();
 
-    private GeneManager geneData;
+    /**
+     * Attribute used to group tracks.  Normally "null".  Set from the "Tracks" menu.
+     */
+    private String groupByAttribute = null;
 
     /**
-     * Object representing the complete set of genes for the selected genomeId
-     *
-     * @return TrackManager
+     * The gene track for the current genome, rendered in the FeaturePanel
      */
-    public static TrackManager getInstance() {
-        if (theInstance == null) {
-            theInstance = new TrackManager();
-        }
+    private Track geneTrack;
 
-        return theInstance;
-    }
+    private Map<String, List<Track>> overlayTracksMap = new HashMap();
 
-    private TrackManager() {
+    private Set<TrackType> loadedTypes = new HashSet();
+    public static final String DATA_PANEL_NAME = "DataPanel";
+    public static final String FEATURE_PANEL_NAME = "FeaturePanel";
 
-        // Create default groups for each  panel
-        panelTrackGroups = new Hashtable();
-        trackGroups = new Hashtable();
-        initGroupsForPanel(IGVMainFrame.DATA_PANEL_NAME);
-        initGroupsForPanel(IGVMainFrame.FEATURE_PANEL_NAME);
+    public Track getGeneTrack() {
+        return geneTrack;
     }
 
-    public void initGroupsForPanel(String panelName) {
-        TrackGroup allDataTracks = new TrackGroup();
-        allDataTracks.setDrawBorder(false);
-        panelTrackGroups.put(panelName, allDataTracks);
-
-        trackGroups.put(panelName, new LinkedHashMap());
+    public Set<TrackType> getLoadedTypes() {
+        return loadedTypes;
     }
 
-    public Track getTrackById(String id) {
-        return trackDictionary.get(id);
-    }
 
-    public List<Track> getTracksForPanel(String panelName) {
-        return panelTrackGroups.containsKey(panelName) ? panelTrackGroups.get(panelName).getTracks() : emptyList;
-    }
+    public TrackManager(IGVMainFrame mainFrame) {
 
-    public Collection<String> getPanelNames() {
-        return panelTrackGroups.keySet();
+        this.mainFrame = mainFrame;
+        loader = new TrackLoader();
     }
 
-    /**
-     * Method description
-     *
-     * @param newGeneTrack
-     */
-    public void setGeneTrack(Track newGeneTrack) {
-
-        boolean foundGeneTrack = false;
-        for (TrackGroup g : panelTrackGroups.values()) {
-            if (g.contains(geneTrack)) {
-                int geneTrackIndex = g.indexOf(geneTrack);
-                g.remove(geneTrack);
-                geneTrackIndex = Math.min(g.size(), geneTrackIndex);
-                g.add(geneTrackIndex, newGeneTrack);
-                foundGeneTrack = true;
-                break;
-            }
-        }
+    public void putScrollPane(String name, TrackPanelScrollPane sp) {
+        trackPanelScrollPanes.put(name, sp);
+    }
 
-        if (!foundGeneTrack) {
-            if (PreferenceManager.getInstance().isShowSingleTrackPane()) {
-                panelTrackGroups.get(IGVMainFrame.DATA_PANEL_NAME).add(newGeneTrack);
-            } else {
-                panelTrackGroups.get(IGVMainFrame.FEATURE_PANEL_NAME).add(newGeneTrack);
-            }
-        }
+    public TrackPanelScrollPane getScrollPane(String name) {
+        return trackPanelScrollPanes.get(name);
+    }
 
-        // Keep a reference to this track so it can be removed
-        geneTrack = newGeneTrack;
+    public Collection<TrackPanelScrollPane> getTrackPanelScrollPanes() {
+        return trackPanelScrollPanes.values();
+    }
 
-        groupTracksByAttribute();
+    public void removeScrollPane(String name) {
+        trackPanelScrollPanes.remove(name);
     }
 
-    /**
-     * Return the track groups.
-     *
-     * @return
-     */
-    public Collection<TrackGroup> getTrackGroups(String panelName) {
-        Collection<TrackGroup> group = trackGroups.get(panelName).values();
-        return group;
+    public void clearScrollPanes() {
+        trackPanelScrollPanes.clear();
     }
 
+
     /**
      * Method description
      *
      * @param attributeName
      */
     public void setGroupByAttribute(String attributeName) {
-        this.groupByAttribute = attributeName;
+        groupByAttribute = attributeName;
         groupTracksByAttribute();
     }
 
-    /**
-     * Method description
-     */
-    public void groupTracksByAttribute() {
-
-        for (Map.Entry<String, Map<String, TrackGroup>> entry : trackGroups.entrySet()) {
-            String panelName = entry.getKey();
-            Map<String, TrackGroup> groupsMap = entry.getValue();
-            groupsMap.clear();
-
-            if (getGroupByAttribute() == null) {
-                groupsMap.put("", panelTrackGroups.get(panelName));
-            } else {
-                for (Track track : panelTrackGroups.get(panelName).getTracks()) {
-                    String attributeValue = track.getAttributeValue(
-                            getGroupByAttribute());
-
-                    if (attributeValue == null) {
-                        attributeValue = "";
-                    }
-
-                    TrackGroup group = groupsMap.get(attributeValue);
-
-                    if (group == null) {
-                        group = new TrackGroup(getGroupByAttribute(), attributeValue);
-                        groupsMap.put(attributeValue, group);
-                    }
-
-                    group.add(track);
-                }
-            }
+    public void reset() {
+        groupByAttribute = "";
+        TrackPanelScrollPane tsp = trackPanelScrollPanes.get(DATA_PANEL_NAME);
+        if (tsp != null) {
+            tsp.getTrackPanel().reset();
         }
     }
 
-    /**
-     * @return
-     * @deprecated -- keep until Session is refactored to break dependence on panels
-     */
-    public List<Track> getDataTracks() {
-        return panelTrackGroups.get(IGVMainFrame.DATA_PANEL_NAME).getTracks();
-    }
-
-    /**
-     * Method description
-     */
-    public void resetApplication() {
-
-        // Remove Tracks
-        clearTracks();
-
-        // Remove loaded Attributes
-        AttributeManager.getInstance().clearAllAttributes();
-    }
-
-    /**
-     * Method description
-     */
-    public void clearTracks() {
 
-        setGroupByAttribute(null);
-        for (TrackGroup tg : panelTrackGroups.values()) {
-            tg.clear();
+    public void groupTracksByAttribute() {
+        //for (TrackPanelScrollPane tsp : getTrackPanelScrollPanes()) {
+        //    TrackPanel trackPanel = tsp.getTrackPanel();
+        //    trackPanel.groupTracksByAttribute(groupByAttribute);
+        //}
+        TrackPanelScrollPane tsp = trackPanelScrollPanes.get(DATA_PANEL_NAME);
+        if (tsp != null) {
+            tsp.getTrackPanel().groupTracksByAttribute(groupByAttribute);
         }
-        setGeneTrack(geneTrack);
-
     }
 
+
     /**
      * A (hopefully) temporary solution to force SAM track reloads,  until we have a better
      * dependency scheme
@@ -290,18 +163,18 @@ public class TrackManager {
         }
     }
 
-    public void preloadSAMTracks() {
+    public void sortAlignmentTracks(AlignmentTrack.SortOption option) {
         for (Track t : getAllTracks(false)) {
             if (t instanceof AlignmentTrack) {
-                ((AlignmentTrack) t).preloadData();
+                ((AlignmentTrack) t).sortRows(option);
             }
         }
     }
 
-    public void sortAlignmentTracks(AlignmentTrack.SortOption option) {
+    public void sortAlignmentTracks(AlignmentTrack.SortOption option, double location) {
         for (Track t : getAllTracks(false)) {
             if (t instanceof AlignmentTrack) {
-                ((AlignmentTrack) t).sortRows(option);
+                ((AlignmentTrack) t).sortRows(option, location);
             }
         }
     }
@@ -320,32 +193,41 @@ public class TrackManager {
         }
     }
 
-    public boolean isSinglePanel() {
-        return PreferenceManager.getInstance().isShowSingleTrackPane();
+
+    public void expandTracks() {
+        for (Track t : getAllTracks(true)) {
+            t.setExpanded(true);
+        }
     }
 
-    public boolean loadResources(Collection<ResourceLocator> locators) {
-        return loadResources(locators, false);
+    public void collapseTrack(String trackName) {
+        for (Track t : getAllTracks(true)) {
+            if (t.getName().equals(trackName)) {
+                t.setExpanded(false);
+            }
+        }
     }
 
-    public boolean loadResources(Collection<ResourceLocator> locators, boolean replace) {
-        boolean tracksWereLoaded = false;
 
-        Set<ResourceLocator> loadedResources = new HashSet();
-        if (replace) {
-            for (Track t : getAllTracks(true)) {
-                loadedResources.add(t.getResourceLocator());
+    public void expandTrack(String trackName) {
+        for (Track t : getAllTracks(true)) {
+            if (t.getName().equals(trackName)) {
+                t.setExpanded(true);
             }
         }
+    }
+
 
-        log.info("Loading" + locators.size() + " tracks.");
+    public boolean loadResources(Collection<ResourceLocator> locators) {
+
+        boolean tracksWereLoaded = false;
+
+        log.info("Loading" + locators.size() + " resources.");
         MessageCollection messages = new MessageCollection();
 
 
         for (ResourceLocator locator : locators) {
-            if (replace && loadedResources.contains(locator)) {
-                continue;
-            }
+
             if (locator.isLocal()) {
                 File trackSetFile = new File(locator.getPath());
 
@@ -354,59 +236,81 @@ public class TrackManager {
                     continue;
                 }
             }
-            TrackGroup group = getTrackGroupFor(locator);
-
+            TrackPanel group = getPanelFor(locator);
             try {
-                load(locator, group, messages);
-            } catch (DataLoadException dle) {
-                messages.append(dle.getMessage() + "\n");
+                List<Track> tracks = load(locator);
+                group.addTracks(tracks);
+                tracksWereLoaded = tracks.size() > 0;
+            } catch (Throwable e) {
+                log.error("Error loading tracks", e);
+                messages.append(e.getMessage());
             }
         }
 
         groupTracksByAttribute();
         resetOverlayTracks();
-        for (Track t : this.getAllTracks(true)) {
-            t.updateProperties();
-        }
 
-        tracksWereLoaded = true;
+        //TODO Throw the message window higher up. There should be a throw when you are using the UI,
+        // not when you are connecting through Socket
 
-        trackDictionary = new Hashtable();
-        for (Track t : this.getAllTracks(true)) {
-            trackDictionary.put(t.getId(), t);
+        if (!messages.isEmpty()) {
+            for (String message : messages.getMessages()) {
+                MessageUtils.showMessage(message);
+            }
         }
+        return tracksWereLoaded;
+    }
 
-        // Need this because TrackManager loads some attributes
-        // internally. So we need to pick them up.
-        IGVMainFrame.getInstance().updateAttributePanel();
-        if (!messages.isEmpty()) {
-            UIUtilities.showAndLogErrorMessage(IGVMainFrame.getInstance(),
-                    messages.getFormattedMessage(), log);
+    /**
+     * Load a resource (track or sample attribute file)
+     */
+
+    public List<Track> load(ResourceLocator locator) {
+
+        try {
+            List<Track> newTracks = loader.load(locator);
+            if (newTracks.size() > 0) {
+                for (Track track : newTracks) {
+                    track.setSourceFile(locator.getPath());
+                    if (locator.getUrl() != null) {
+                        track.setUrl(locator.getUrl());
+                    }
+                    track.setAttributeValue("NAME", track.getName());
+                    track.setAttributeValue("DATA FILE", locator.getPath());
+                    track.setAttributeValue("DATA TYPE", track.getTrackType().toString());
+
+
+                }
+            }
+            return newTracks;
+
+        } catch (DataLoadException dle) {
+            throw dle;
+        } catch (Exception e) {
+            log.error(e);
+            throw new DataLoadException(e.getMessage(), locator.getPath());
         }
 
-        return tracksWereLoaded;
     }
 
-    private boolean isSamFile(String path) {
-        return path.endsWith("sam") || path.endsWith("sam.group") ||
-                path.endsWith("bam");
+    /**
+     * @param type
+     */
+    public void addLoadedType(TrackType type) {
+        loadedTypes.add(type);
     }
 
     /**
-     * Load the data file into the specified group.
+     * Load the data file into the specified panel.   Triggered via drag and drop.
      *
      * @param file
-     * @param panelId
+     * @param panel
      * @return
      */
-    public MessageCollection load(File file, String panelId) {
-        MessageCollection messages = new MessageCollection();
-
-        TrackGroup group = panelTrackGroups.get(panelId);
+    public void load(File file, TrackPanel panel) {
         ResourceLocator locator = new ResourceLocator(file.getAbsolutePath());
-        load(locator, group, messages);
-
-        return messages;
+        List<Track> tracks = load(locator);
+        panel.addTracks(tracks);
     }
 
     /**
@@ -421,13 +325,12 @@ public class TrackManager {
     public void resetOverlayTracks() {
         overlayTracksMap.clear();
 
-        if (isDoOverlays()) {
-            String overlayAttribute = getOverlayAttribute();
-
+        if (PreferenceManager.getInstance().getOverlayTracks()) {
+            String overlayAttribute = IGVMainFrame.getInstance().getSession().getOverlayAttribute();
             if (overlayAttribute != null) {
                 for (Track track : getAllTracks(false)) {
                     if (track != null) {
-                        if (track.getTrackType() == IGVConstants.overlayTrackType) {
+                        if (track.getTrackType() == UIConstants.overlayTrackType) {
                             String value = track.getAttributeValue(overlayAttribute);
 
                             if (value != null) {
@@ -446,11 +349,11 @@ public class TrackManager {
             }
         }
 
-        boolean displayOverlays = PreferenceManager.getInstance().getDiplayOverlayTracks();
+        boolean displayOverlays = IGVMainFrame.getInstance().getSession().getDisplayOverlayTracks();
 
         for (Track track : getAllTracks(false)) {
             if (track != null) {
-                if (track.getTrackType() == IGVConstants.overlayTrackType) {
+                if (track.getTrackType() == UIConstants.overlayTrackType) {
                     track.setOverlayVisible(displayOverlays);
                 }
             }
@@ -458,30 +361,20 @@ public class TrackManager {
         }
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public boolean hasOverlays() {
-        return !overlayTracksMap.isEmpty();
-    }
-
-    /**
-     * @return
-     * @deprecated Keep until Session is refactored
-     */
-    public List<Track> getFeatureTracks() {
-        return panelTrackGroups.get(IGVMainFrame.FEATURE_PANEL_NAME).getTracks();
-    }
 
     /**
      * Method description
      *
      * @return
      */
-    public int getTrackCount() {
-        return getAllTracks(true).size();
+    public int getVisibleTrackCount() {
+        int count = 0;
+        for (TrackPanelScrollPane tsp : getTrackPanelScrollPanes()) {
+            TrackPanel tsv = tsp.getTrackPanel();
+            count += tsv.getVisibleTrackCount();
+
+        }
+        return count;
     }
 
     /**
@@ -493,8 +386,9 @@ public class TrackManager {
     public List<Track> getAllTracks(boolean includeGeneTrack) {
         List<Track> allTracks = new ArrayList<Track>();
 
-        for (TrackGroup tg : panelTrackGroups.values()) {
-            allTracks.addAll(tg.getTracks());
+        for (TrackPanelScrollPane tsp : getTrackPanelScrollPanes()) {
+            TrackPanel tsv = tsp.getTrackPanel();
+            allTracks.addAll(tsv.getTracks());
         }
         if ((geneTrack != null) && !includeGeneTrack) {
             allTracks.remove(geneTrack);
@@ -503,30 +397,11 @@ public class TrackManager {
         return allTracks;
     }
 
-    public List<DataSourceTrack> getAllDataSourceTracks() {
-        List<DataSourceTrack> allTracks = new ArrayList();
-
-        for (TrackGroup tg : panelTrackGroups.values()) {
-            for (Track t : tg.getTracks()) {
-                if (t instanceof DataSourceTrack) {
-                    allTracks.add((DataSourceTrack) t);
-                }
-            }
-        }
-
-        return allTracks;
-    }
-
     public void clearSelections() {
-        if (log.isDebugEnabled()) {
-            log.debug("enter clearTrackSelections");
+        for (Track t : getAllTracks(true)) {
+            t.setSelected(false);
         }
 
-        for (TrackGroup tg : panelTrackGroups.values()) {
-            for (Track t : tg.getTracks()) {
-                t.setSelected(false);
-            }
-        }
     }
 
     public void setTrackSelections(Set<Track> selectedTracks) {
@@ -566,7 +441,7 @@ public class TrackManager {
     }
 
     public Collection<Track> getSelectedTracks() {
-        List<Track> selectedTracks = new ArrayList();
+        HashSet<Track> selectedTracks = new HashSet();
         for (Track t : getAllTracks(true)) {
             if (t.isSelected()) {
                 selectedTracks.add(t);
@@ -603,59 +478,56 @@ public class TrackManager {
      */
     public void setAllTrackHeights(int newHeight) {
         for (Track track : this.getAllTracks(false)) {
-            track.setHeight((int) newHeight);
+            track.setHeight(newHeight);
         }
 
     }
 
+
+    public void unloadTracks(Collection<ResourceLocator> locators) {
+
+        Collection<Track> tracksToRemove = new ArrayList();
+        HashSet<ResourceLocator> locatorSet = new HashSet(locators);
+        for (Track t : getAllTracks(true)) {
+            if (locatorSet.contains(t.getResourceLocator())) {
+                tracksToRemove.add(t);
+            }
+        }
+        removeTracks(tracksToRemove);
+    }
+
     /**
      * Method description
      *
      * @param tracksToRemove
      */
     public void removeTracks(Collection<Track> tracksToRemove) {
-        for (TrackGroup tg : panelTrackGroups.values()) {
-
-            Set<Track> tmp = new HashSet<Track>(tracksToRemove);
-            // TODO -- replace with track.getDependentTracks(), or similar
-            for (Track t : tracksToRemove) {
-                trackDictionary.remove(t.getId());
-                if (t instanceof AlignmentTrack) {
-                    CoverageTrack ct = ((AlignmentTrack) t).getCoverageTrack();
-                    tmp.add(ct);
-                    trackDictionary.remove(ct.getId());
-                }
+
+        Set<Track> tmp = new HashSet<Track>(tracksToRemove);
+        // TODO -- replace with track.getDependentTracks(), or similar
+        for (Track t : tracksToRemove) {
+            if (t instanceof AlignmentTrack) {
+                CoverageTrack ct = ((AlignmentTrack) t).getCoverageTrack();
+                tmp.add(ct);
             }
+        }
 
-            tg.removeAll(tmp);
+        for (TrackPanelScrollPane tsp : getTrackPanelScrollPanes()) {
+            TrackPanel tsv = tsp.getTrackPanel();
+            tsv.removeTracks(tmp);
 
 
+            if (!tsv.hasTracks()) {
+                mainFrame.removeDataPanel(tsp.getName());
+            }
         }
 
-        for (Track t : tracksToRemove) {
+        for (Track t : tmp) {
             if (t instanceof DragListener) {
                 DragEventManager.getInstance().removeDragListener((DragListener) t);
             }
 
         }
-        groupTracksByAttribute();
-
-        // If the last track of a user defined panel is removed, remove the panel.
-        List<String> panelsToRemove = new ArrayList();
-        for (Map.Entry<String, TrackGroup> entry : panelTrackGroups.entrySet()) {
-            if (entry.getValue().getTracks().isEmpty() &&
-                    !entry.getKey().equals(IGVMainFrame.DATA_PANEL_NAME) &&
-                    !entry.getKey().equals(IGVMainFrame.FEATURE_PANEL_NAME)) {
-                panelsToRemove.add(entry.getKey());
-            }
-        }
-
-        for (String panelName : panelsToRemove) {
-            panelTrackGroups.remove(panelName);
-            trackGroups.remove(panelName);
-            IGVMainFrame.getInstance().removeDataPanel(panelName);
-        }
-
     }
 
     /**
@@ -665,206 +537,42 @@ public class TrackManager {
      * @param attributeNames
      * @param ascending
      */
-    public void sortAllTracksByAttributes(final String attributeNames[],
-                                          final boolean[] ascending) {
+    public void sortAllTracksByAttributes(final String attributeNames[], final boolean[] ascending) {
         assert attributeNames.length == ascending.length;
 
-        for (TrackGroup tg : panelTrackGroups.values()) {
-            sortByAttributes(tg.getTracks(), attributeNames, ascending);
-        }
-        for (Map<String, TrackGroup> panelGroups : trackGroups.values()) {
-            for (TrackGroup tg : panelGroups.values()) {
-                sortByAttributes(tg.getTracks(), attributeNames, ascending);
-            }
+        for (TrackPanelScrollPane tsp : getTrackPanelScrollPanes()) {
+            TrackPanel tsv = tsp.getTrackPanel();
+            tsv.sortTracksByAttributes(attributeNames, ascending);
         }
     }
 
     public void sortAllTracksByPosition(List<String> trackIds) {
-        for (TrackGroup tg : panelTrackGroups.values()) {
-            sortByList(tg.getTracks(), trackIds);
+        for (TrackPanelScrollPane tsp : getTrackPanelScrollPanes()) {
+            TrackPanel tsv = tsp.getTrackPanel();
+            tsv.sortTracksByPosition(trackIds);
         }
-        for (Map<String, TrackGroup> panelGroups : trackGroups.values()) {
-            for (TrackGroup tg : panelGroups.values()) {
-                sortByList(tg.getTracks(), trackIds);
-            }
-        }
-    }
-
-    public synchronized void moveSelectedTracksTo(Collection<Track> selectedTracks,
-                                                  String target, Track targetTrack, boolean before) {
-
-        if (selectedTracks.isEmpty()) {
-            return;
-        }
-
-        // The targetPanelTracks is the track collection for the panel we are dropping to
-        TrackGroup targetTrackGroup = panelTrackGroups.get(target);
-        List<Track> targetPanelTracks = targetTrackGroup.getTracks();
-
-
-        // A targetTrack of null => drop at end of track
-        int index = targetTrack == null ? targetPanelTracks.size() : targetPanelTracks.indexOf(
-                targetTrack);
-        if (!before) {
-            index = index + 1;
-        }
-
-        // 1. Divdide the target list up into 2 parts, one before the index and one after
-        List<Track> beforeList = new ArrayList(targetPanelTracks.subList(0,
-                index));
-        List<Track> afterList = new ArrayList(targetPanelTracks.subList(
-                index, targetPanelTracks.size()));
-
-        // 2.  Remove the selected tracks from anywhere they occur
-        beforeList.removeAll(selectedTracks);
-        afterList.removeAll(selectedTracks);
-        for (TrackGroup tg : panelTrackGroups.values()) {
-            if (tg != targetTrackGroup) {
-                tg.getTracks().removeAll(selectedTracks);
-            }
-        }
-
-        // 3. Now insert the selected tracks
-        targetPanelTracks.clear();
-        targetPanelTracks.addAll(beforeList);
-        targetPanelTracks.addAll(selectedTracks);
-        targetPanelTracks.addAll(afterList);
-
-
-        // 3.  Clear selections ?
-        // clearTrackSelections();
-
-
-    }
-
-    static int panelNumber = 1;
-
-    private synchronized int getNextPanelNumber() {
-        return panelNumber++;
     }
 
-    protected TrackGroup getTrackGroupFor(ResourceLocator locator) {
+    public TrackPanel getPanelFor(ResourceLocator locator) {
         String path = locator.getPath().toLowerCase();
         if (PreferenceManager.getInstance().isShowSingleTrackPane()) {
-            return panelTrackGroups.get(IGVMainFrame.DATA_PANEL_NAME);
+            return mainFrame.getDataPanel(DATA_PANEL_NAME);
         } else if (path.endsWith(".sam") || path.endsWith(".bam") ||
-                path.endsWith(".sam.list") || path.endsWith(".maf") ||
+                path.endsWith(".sam.list") || path.endsWith(".bam.list") ||
                 path.endsWith(".aligned") || path.endsWith(".sorted.txt")) {
 
-            String newPanelName = "Panel" + getNextPanelNumber();
-            IGVMainFrame.getInstance().addDataPanel(newPanelName);
-            return panelTrackGroups.get(newPanelName);
+            String newPanelName = "Panel" + System.currentTimeMillis();
+            return mainFrame.addDataPanel(newPanelName).getTrackPanel();
         } else {
-            return getDefaultGroup(locator);
+            return getDefaultPanel(locator);
         }
     }
 
-    /**
-     * Load a resource (track or sample attribute file) into the specified
-     * track group.
-     */
-
-    private void load(ResourceLocator locator, TrackGroup group, MessageCollection messages) {
-
-        try {
-            String typeString = locator.getType();
-            if (typeString == null) {
-                typeString = locator.getPath().toLowerCase();
-                if (!typeString.endsWith("_sorted.txt") &&
-                        (typeString.endsWith(".txt") || typeString.endsWith(
-                                ".xls") || typeString.endsWith(".gz"))) {
-                    typeString = typeString.substring(0, typeString.lastIndexOf("."));
-                }
-            }
-
-            List<Track> newTracks = new ArrayList();
-
-            if (typeString.equals("DAS") || typeString.endsWith("das")) {
-                loadDASResource(locator, newTracks);
-            } else if (typeString.equals("PLINK")) {
-
-            } else if (typeString.endsWith("h5") || typeString.endsWith("hbin")) {
-                loadH5File(locator, newTracks);
-            } else if (typeString.endsWith("rnai.gct")) {
-                loadRnaiGctFile(locator, newTracks);
-            } else if (typeString.endsWith("gct") || typeString.endsWith("res") || typeString.endsWith(
-                    "tab")) {
-                loadGctFile(locator, newTracks);
-            } else if (typeString.endsWith("cn") || typeString.endsWith("xcn") || typeString.endsWith(
-                    "snp") || typeString.endsWith("igv") || typeString.endsWith("loh")) {
-                loadIGVFile(locator, newTracks);
-            } else if (typeString.endsWith("mut")) {
-                loadMutFile(locator, newTracks);
-            } else if (typeString.endsWith("cbs") || typeString.endsWith("seg") || typeString.endsWith(
-                    "glad")) {
-                loadSegFile(locator, newTracks);
-            } else if (typeString.endsWith("seg.zip")) {
-                loadBinarySegFile(locator, newTracks);
-            } else if (typeString.endsWith("gistic")) {
-                loadGisticFile(locator, newTracks);
-            } else if (typeString.endsWith(".gs")) {
-                loadRNAiGeneScoreFile(locator, newTracks,
-                        RNAIGeneScoreParser.Type.GENE_SCORE);
-            } else if (typeString.endsWith("riger")) {
-                loadRNAiGeneScoreFile(locator, newTracks,
-                        RNAIGeneScoreParser.Type.POOLED);
-            } else if (typeString.endsWith(".hp")) {
-                loadRNAiHPScoreFile(locator);
-            } else if (typeString.endsWith("gene")) {
-                loadGeneFile(locator, newTracks);
-            } else if (typeString.contains("tabblastn") ||
-                    typeString.endsWith("orthologs")) {
-                loadSyntentyMapping(locator, newTracks);
-            } else if (typeString.endsWith(".sam") || typeString.endsWith(".bam") ||
-                    typeString.endsWith(".sam.list") || typeString.endsWith("_sorted.txt") ||
-                    typeString.endsWith(".aligned") || typeString.endsWith(".sai") ||
-                    typeString.endsWith(".bai") || typeString.endsWith(".bedz")) {
-                loadAlignmentsTrack(locator, newTracks);
-            } else if (typeString.endsWith(".maf")) {
-                loadMAFTrack(locator, newTracks);
-            } else if (typeString.endsWith("omega")) {
-                loadOmegaTrack(locator, newTracks);
-            } else if (typeString.endsWith("wig")) {
-                loadWigFile(locator, newTracks);
-            } else if (typeString.endsWith("list")) {
-                loadListFile(locator, newTracks);
-            } else if (typeString.endsWith("list")) {
-                loadListFile(locator, newTracks);
-            } else if (typeString.contains("dranger")) {
-                loadDRangerFile(locator, newTracks);
-            } else if (typeString.endsWith("ewig.tdf") || (typeString.endsWith("ewig.ibf"))) {
-                loadEwigIBFFile(locator, newTracks);
-            } else if (typeString.endsWith(".ibf") || typeString.endsWith(".tdf")) {
-                loadTDFFile(locator, newTracks);
-            } else {
-                loadFeatureOrAttributeFile(locator, newTracks);
-            }
-
-            String trackSetName = locator.getName();
-
-            if (trackSetName == null) {
-                trackSetName = new File(locator.getPath()).getName();
-            }
-
-            for (Track track : newTracks) {
-                track.setSourceFile(locator.getPath());
-                group.add(track);
-                regsiterAttributes(track, trackSetName);
-            }
-
-        } catch (DataLoadException dle) {
-            throw dle;
-        } catch (Exception e) {
-            log.error("Error loading file: " + locator.getName(), e);
-            throw new DataLoadException(e.getMessage(), locator.getPath());
-        }
-
-    }
 
-    private TrackGroup getDefaultGroup(ResourceLocator locator) {
+    private TrackPanel getDefaultPanel(ResourceLocator locator) {
 
         if (locator.getType() != null && locator.getType().equalsIgnoreCase("das")) {
-            return panelTrackGroups.get(IGVMainFrame.FEATURE_PANEL_NAME);
+            return mainFrame.getDataPanel(FEATURE_PANEL_NAME);
         }
 
         String filename = locator.getPath().toLowerCase();
@@ -881,747 +589,12 @@ public class TrackManager {
                 filename.endsWith("gff") || filename.endsWith("gtf") ||
                 filename.endsWith("gff3") || filename.endsWith("embl") ||
                 filename.endsWith("bed") || filename.endsWith("gistic") ||
-                filename.endsWith("bedz") ||
+                filename.endsWith("bedz") || filename.endsWith("repmask") ||
                 filename.contains("dranger")) {
-            return panelTrackGroups.get(IGVMainFrame.FEATURE_PANEL_NAME);
-        } /*
-        if (filename.endsWith("h5") || filename.endsWith("h5.list") ||
-        filename.endsWith("hbin") || filename.endsWith("gct") ||
-        filename.endsWith("res") || filename.endsWith("dchip") ||
-        filename.endsWith("cn") || filename.endsWith("xcn") ||
-        filename.endsWith("snp") || filename.endsWith("igv") ||
-        filename.endsWith("wig") || filename.endsWith("mut") ||
-        filename.endsWith("cbs") || filename.endsWith("seg") ||
-        filename.endsWith("glad") || filename.endsWith("seg.zip") ||
-        filename.endsWith("dzip") ||
-        filename.endsWith(".gs") || filename.endsWith("riger") ||
-        filename.endsWith(".hp") || filename.endsWith(".sam") ||
-        filename.endsWith(".bam") || filename.endsWith(".sam.group")) {
-        return panelTrackGroups.get(IGVMainFrame.DATA_PANEL_NAME);
-        } */
-        else {
-            return panelTrackGroups.get(IGVMainFrame.DATA_PANEL_NAME);
-        }
-    }
-
-    /**
-     * Load the input file as a BED or Attribute (Sample Info) file.  First assume
-     * it is a BED file,  if no features are found load as an attribute file.
-     *
-     * @param locator
-     * @param newTracks
-     */
-    private void loadGeneFile(ResourceLocator locator, List<Track> newTracks) {
-
-        String fn = locator.getPath().toUpperCase();
-
-        FeatureParser featureParser = AbstractFeatureParser.getInstanceFor(locator);
-        List<FeatureTrack> tracks = featureParser.loadTracks(
-                locator);
-        newTracks.addAll(tracks);
-
-    }
-
-    private void loadSyntentyMapping(ResourceLocator locator, List<Track> newTracks) {
-        List<Mapping> mappings = (new MappingParser()).parse(locator.getPath());
-        List<Feature> features = new ArrayList(mappings.size());
-        features.addAll(mappings);
-        String fName = locator.getDisplayName();
-
-        Track track = new FeatureTrack(locator, fName, features);
-
-        // track.setRendererClass(AlignmentBlockRenderer.class);
-        newTracks.add(track);
-    }
-
-    private void loadDRangerFile(ResourceLocator locator, List<Track> newTracks) {
-
-        DRangerParser parser = new DRangerParser();
-        newTracks.addAll(parser.loadTracks(locator));
-    }
-
-
-    /**
-     * Load the input file as a feature or attribute (Sample Info) file.  First assume
-     * it is a BED file,  if no features are found load as an attribute file.
-     *
-     * @param locator
-     * @param newTracks
-     */
-    private void loadFeatureOrAttributeFile(ResourceLocator locator, List<Track> newTracks) {
-
-        FeatureParser featureParser = AbstractFeatureParser.getInstanceFor(locator);
-        if (GCTDatasetParser.parsableMAGE_TAB(locator)) {
-            locator.setDescription("MAGE_TAB");
-            loadGctFile(locator, newTracks);
-        } else if (IGVDatasetParser.parsableMAGE_TAB(locator)) {
-            locator.setDescription("MAGE_TAB");
-            loadIGVFile(locator, newTracks);
-        } else if (featureParser.isFeatureFile(locator)) {
-            List<FeatureTrack> tracks = featureParser.loadTracks(locator);
-            newTracks.addAll(tracks);
-        } else if (WiggleParser.isWiggle(locator)) {
-            loadWigFile(locator, newTracks);
-        } else {
-            loadAttributeFile(locator);
-            for (Track track : this.getAllTracks(true)) {
-            }
-        }
-    }
-
-    /**
-     * Load the input file as a BED or Attribute (Sample Info) file.  First assume
-     * it is a BED file,  if no features are found load as an attribute file.
-     *
-     * @param locator
-     */
-    private void loadAttributeFile(ResourceLocator locator) {
-
-        HashSet attributeList = AttributeManager.getInstance().loadAttributes(locator);
-
-        if (attributeList != null) {
-
-            // TODO -- temporary until "name" property is added to locator
-            String fName = locator.getDisplayName();
-
-        }
-    }
-
-    private void loadRnaiGctFile(ResourceLocator locator, List<Track> newTracks) {
-
-        RNAIGCTDatasetParser parser = new RNAIGCTDatasetParser(locator);
-
-        Collection<RNAIDataSource> dataSources = parser.parse();
-        if (dataSources != null) {
-            for (RNAIDataSource ds : dataSources) {
-                Track track = new DataSourceTrack(locator, ds.getDisplayName(), ds);
-
-                // Set attributes.  This "hack" is neccessary to register these attributes with the
-                // attribute manager to get displayed.
-                track.setAttributeValue("SCREEN", ds.getScreen());
-
-                // Set RNAi data scale -- TODO -- remove hardcoded values or make constants.
-                track.setHeight(80);
-                // track.setDataRange(new DataRange(-3, 0, 3));
-                newTracks.add(track);
-            }
-        }
-    }
-
-    // Hack for demo TODO -- remove
-    public GCTDataset gctDataset;
-
-    private void loadGctFile(ResourceLocator locator, List<Track> newTracks) {
-
-        GCTDatasetParser parser = null;
-        GCTDataset ds = null;
-
-        String fName = locator.getDisplayName();
-
-        // TODO -- handle remote resource
-        try {
-            parser = new GCTDatasetParser(locator, null,
-                    IGVModel.getInstance().getViewContext().getGenomeId());
-        } catch (IOException e) {
-            log.error("Error creating GCT parser.", e);
-            throw new DataLoadException("Error creating GCT parser: " + e, locator.getPath());
-        }
-        ds = parser.createDataset();
-        gctDataset = ds;
-
-        ds.setName(fName);
-        ds.setNormalized(true);
-        ds.setLogValues(true);
-
-        /*
-         * File outputFile = new File(IGVMainFrame.DEFAULT_USER_DIRECTORY, file.getName() + ".h5");
-         * OverlappingProcessor proc = new OverlappingProcessor(ds);
-         * proc.setZoomMax(0);
-         * proc.process(outputFile.getAbsolutePath());
-         * loadH5File(outputFile, messages, attributeList, group);
-         */
-
-        // Counter for generating ID
-        TrackProperties trackProperties = ds.getTrackProperties();
-        for (String trackName : ds.getDataHeadings()) {
-            Genome currentGenome = IGVModel.getInstance().getViewContext().getGenome();
-            DatasetDataSource dataSource = new DatasetDataSource(currentGenome,
-                    trackName, ds);
-
-            Track track = new DataSourceTrack(locator, trackName, dataSource);
-
-            track.setRendererClass(HeatmapRenderer.class);
-
-
-            track.setTrackProperties(trackProperties);
-
-            newTracks.add(track);
-        }
-    }
-
-    private void loadIGVFile(ResourceLocator locator, List<Track> newTracks) {
-
-        String dsName = locator.getDisplayName();
-
-        String currentGenomeId = IGVModel.getInstance().getViewContext().getGenomeId();
-        IGVDataset ds = new IGVDataset(currentGenomeId, locator);
-
-        ds.setName(dsName);
-
-        TrackProperties trackProperties = ds.getTrackProperties();
-        for (String trackName : ds.getDataHeadings()) {
-
-            Genome currentGenome = IGVModel.getInstance().getViewContext().getGenome();
-            DatasetDataSource dataSource = new DatasetDataSource(currentGenome, trackName, ds);
-            DataSourceTrack track = new DataSourceTrack(locator, trackName, dataSource);
-
-            // track.setRendererClass(HeatmapRenderer.class);
-            track.setTrackType(ds.getType());
-
-            track.setTrackProperties(trackProperties);
-
-            newTracks.add(track);
-        }
-    }
-
-    private void loadWigFile(ResourceLocator locator, List<Track> newTracks) {
-
-        String genome = IGVModel.getInstance().getViewContext().getGenomeId();
-
-        WiggleDataset ds = (new WiggleParser(locator, genome)).parse();
-        TrackProperties trackProperties = ds.getTrackProperties();
-
-        for (String heading : ds.getDataHeadings()) {
-            String trackName = heading;
-            if (trackProperties.getName() != null) {
-                trackName = trackProperties.getName();
-            }
-
-            Genome currentGenome = IGVModel.getInstance().getViewContext().getGenome();
-            DatasetDataSource dataSource = new DatasetDataSource(currentGenome,
-                    trackName, ds);
-            Track track = new DataSourceTrack(locator, trackName, dataSource);
-            track.setTrackProperties(trackProperties);
-
-            track.setTrackType(ds.getType());
-            newTracks.add(track);
-        }
-    }
-
-    private void loadTDFFile(ResourceLocator locator, List<Track> newTracks) {
-
-
-        if (log.isDebugEnabled()) {
-            log.debug("Loading TDFFile: " + locator.toString());
-        }
-
-        TDFReader reader = TDFReader.getReader(locator);
-        TrackType type = reader.getTrackType();
-
-        if (log.isDebugEnabled()) {
-            log.debug("Parsing track line ");
-        }
-        TrackProperties props = null;
-        String trackLine = reader.getTrackLine();
-        if (trackLine != null && trackLine.length() > 0) {
-            props = new TrackProperties();
-            ParsingUtils.parseTrackLine(trackLine, props);
-        }
-
-        int trackNumber = 0;
-        String displayName = reader.getTrackNames().length == 1 ? locator.getName() : null;
-        for (String name : reader.getTrackNames()) {
-            TDFDataSource dataSource = new TDFDataSource(reader, trackNumber, name);
-            Track track = new DataSourceTrack(locator, dataSource.getTrackName(), dataSource);
-            track.setTrackType(type);
-            if (props != null) {
-                track.setTrackProperties(props);
-            }
-            if (displayName != null) {
-                track.setDisplayName(displayName);
-            }
-
-            newTracks.add(track);
-            trackNumber++;
-        }
-    }
-
-    private void loadEwigIBFFile(ResourceLocator locator, List<Track> newTracks) {
-
-
-        TDFReader reader = TDFReader.getReader(locator.getPath());
-        TrackType type = reader.getTrackType();
-
-        TrackProperties props = null;
-        String trackLine = reader.getTrackLine();
-        if (trackLine != null && trackLine.length() > 0) {
-            props = new TrackProperties();
-            ParsingUtils.parseTrackLine(trackLine, props);
-        }
-
-        Track track = new EWigTrack(locator);
-        if (props != null) {
-            track.setTrackProperties(props);
-        }
-        track.setDisplayName(locator.getDisplayName());
-        newTracks.add(track);
-    }
-
-    private void loadListFile(ResourceLocator locator, List<Track> newTracks) {
-        try {
-            // Hack for testing -- TODO remove this
-            if (locator.getPath().endsWith("h5.list")) {
-                Track track = new HDFListTrack(locator, locator.getDisplayName());
-                newTracks.add(track);
-            } else {
-                Track track = new FeatureDirTrack(locator, locator.getDisplayName());
-                newTracks.add(track);
-            }
-
-        } catch (IOException ex) {
-            throw new RuntimeException(ex);
-        }
-
-// OverlappingProcessor proc = new OverlappingProcessor(ds);
-
-// proc.setZoomMax(0);
-// proc.process(outputFile.getAbsolutePath());
-
-// loadH5File(locator, newTracks, messages);
-    }
-
-    private void loadGisticFile(ResourceLocator locator, List<Track> newTracks) {
-
-        GisticTrack track = GisticFileParser.loadData(locator);
-        track.setHeight(GISTIC_TRACK_HEIGHT);
-        track.setDisplayName(locator.getDisplayName());
-        newTracks.add(track);
-    }
-
-    /**
-     * Load the data from an HDF5 file and add resulting tracks to the supplied TrackGroup.
-     * Error messages are appended to the MessageCollection
-     *
-     * @param locator
-     */
-    private void loadH5File(ResourceLocator locator, List<Track> newTracks) {
-
-        TrackSet trackSet = null;
-
-        // TODO -- temporary until "name" property is added to locator
-        String fName = null;
-
-
-        // TODO -- temporary until "name" property is added to locator
-        fName = locator.getDisplayName();
-
-        HDFDataManager dataManager = HDFDataManagerFactory.getDataManager(locator);
-        TrackProperties trackProperties = dataManager.getTrackProperties();
-        String[] trackNames = dataManager.getTrackNames();
-
-        List<Track> tracks = new ArrayList();
-
-        for (int trackNumber = 0; trackNumber < trackNames.length; trackNumber++) {
-            String name = trackNames.length == 1 ? fName : trackNames[trackNumber];
-
-            Track track = null;
-            try {
-                track = new HDFDataTrack(dataManager, locator, name, trackNumber);
-            } catch (FileNotFoundException fe) {
-                throw new RuntimeException(fe);
-            }
-            if (trackProperties != null) {
-                track.setTrackProperties(trackProperties);
-            }
-
-            tracks.add(track);
-        }
-
-        trackSet = new TrackSet(tracks);
-
-        if (trackSet.isEmpty()) {
-            throw new RuntimeException("No data found in file");
-        }
-
-        newTracks.addAll(trackSet.getTracks());
-    }
-
-    /**
-     * Load a rnai gene score file and create a datasource and track.
-     * <p/>
-     * // TODO -- change parser to use resource locator rather than path.
-     *
-     * @param locator
-     * @param newTracks
-     */
-    private void loadRNAiGeneScoreFile(ResourceLocator locator,
-                                       List<Track> newTracks, RNAIGeneScoreParser.Type type) {
-
-        String dsName = locator.getDisplayName();
-
-        String genomeId = IGVModel.getInstance().getViewContext().getGenomeId();
-        RNAIGeneScoreParser parser = new RNAIGeneScoreParser(locator.getPath(),
-                genomeId, type);
-
-        Collection<RNAIDataSource> dataSources = parser.parse();
-        for (RNAIDataSource ds : dataSources) {
-            Track track = new DataSourceTrack(locator, ds.getDisplayName(), ds);
-
-            // Set attributes.  This "hack" is neccessary to register these attributes with the
-            // attribute manager to get displayed.
-            track.setAttributeValue("SCREEN", ds.getScreen());
-            if ((ds.getCondition() != null) && (ds.getCondition().length() > 0)) {
-                track.setAttributeValue("CONDITION", ds.getCondition());
-            }
-
-
-            // Set RNAi data scale -- TODO -- remove hardcoded values or make constants.
-            track.setHeight(80);
-            //track.setDataRange(new DataRange(-3, 0, 3));
-            newTracks.add(track);
-        }
-
-    }
-
-    /**
-     * Load a RNAi haripin score file.  The results of this action are hairpin scores
-     * added to the RNAIDataManager.  Currently no tracks are created for hairpin
-     * scores, although this could change.
-     * <p/>
-     * // TODO -- change parser to use resource locator rather than path.
-     *
-     * @param locator
-     */
-    private void loadRNAiHPScoreFile(ResourceLocator locator) {
-
-        String dsName = locator.getDisplayName();
-
-        (new RNAIHairpinParser(locator.getPath())).parse();
-    }
-
-    private void loadMAFTrack(ResourceLocator locator, List<Track> newTracks) {
-
-        MAFTrack t = new MAFTrack(locator, "Multiple Alignments");
-
-        newTracks.add(t);
-    }
-
-    private void loadOmegaTrack(ResourceLocator locator, List<Track> newTracks) {
-        OmegaDataSource ds = new OmegaDataSource();
-        Track track = new OmegaTrack(locator, "Conservation (Omega)", ds);
-
-        // Set RNAi data scale -- TODO -- remove hardcoded values or make constants.
-        track.setHeight(40);
-        //track.setDataRange(new DataRange(-3, 0, 3));
-        newTracks.add(track);
-    }
-
-    /**
-     * Load a rnai gene score file and create a datasource and track.
-     * <p/>
-     * // TODO -- change parser to use resource locator rather than path.
-     *
-     * @param locator
-     * @param newTracks
-     */
-    private void loadAlignmentsTrack(ResourceLocator locator, List<Track> newTracks) {
-
-        try {
-            String dsName = locator.getDisplayName();
-            String fn = locator.getPath().toLowerCase();
-            boolean isBed = fn.endsWith(".bedz") || fn.endsWith("bedz.gz") ||
-                    fn.endsWith(".bed") || fn.endsWith(".bed.gz");
-
-            // If the user tried to load the index,  look for the file (this is a common mistake)
-            if (locator.getPath().endsWith(".sai") || locator.getPath().endsWith(".bai")) {
-                MessageUtils.showMessage("<html>Attempt to load a SAM/BAM index:  " + locator.getPath() + "<br>Indexes are automatically loaded as required.");
-                return;
-            }
-            AlignmentDataManager dataManager = AlignmentDataManager.getDataManager(locator);
-
-            if (locator.getPath().toLowerCase().endsWith(".bam")) {
-                if (!dataManager.hasIndex()) {
-                    MessageUtils.showMessage("<html>Could not load index file for: " +
-                            locator.getPath() + "<br>  An index file is required for SAM & BAM files.");
-                    return;
-                }
-            }
-
-            AlignmentTrack track = new AlignmentTrack(locator, dsName, dataManager);    // parser.loadTrack(locator, dsName);
-
-            if (isBed) {
-                track.setRenderer(new BedRenderer());
-                track.setHeight(40);
-            }
-
-            // Create coverage track
-            CoverageTrack covTrack = new CoverageTrack(locator.getPath() + "_coverage", "Coverage");
-            covTrack.setVisible(PreferenceManager.getInstance().getSAMPreferences().isShowCoverageTrack());
-            newTracks.add(covTrack);
-            track.setCoverageTrack(covTrack);
-            if (!isBed) {
-                covTrack.setDataManager(dataManager);
-                dataManager.setCoverageTrack(covTrack);
-            }
-
-            // Search for precalculated coverage data
-            String covPath = locator.getCoverage();
-            if (covPath == null) {
-                String path = locator.getPath();
-                covPath = path + ".tdf";
-            }
-            if (covPath != null) {
-                try {
-                    if ((new File(covPath)).exists() || (covPath.startsWith("http") && HttpUtils.resourceAvailable(new URL(covPath)))) {
-                        TDFReader reader = TDFReader.getReader(covPath);
-                        TDFDataSource ds = new TDFDataSource(reader, 0, track.getDisplayName() + " coverage");
-                        covTrack.setDataSource(ds);
-                    }
-                } catch (MalformedURLException e) {
-                    // This is expected if
-                    //    log.info("Could not loading coverage data: MalformedURL: " + covPath);
-                }
-            }
-
-            newTracks.add(track);
-            track.preloadData();
-
-        } catch (Exception e) {
-            log.error("Error loading sam track", e);
-            throw new RuntimeException(e);
-        }
-
-    }
-
-
-    /**
-     * Load a ".mut" file (muation file) and create tracks.
-     *
-     * @param locator
-     * @param newTracks
-     */
-    private void loadMutFile(ResourceLocator locator, List<Track> newTracks) {
-
-        List<Track> mutationTracks = MutationParser.loadMutationTracks(locator);
-
-        for (Track track : mutationTracks) {
-            track.setTrackType(TrackType.MUTATION);
-            track.setRendererClass(MutationRenderer.class);
-            newTracks.add(track);
-        }
-    }
-
-    private void loadSegFile(ResourceLocator locator, List<Track> newTracks) {
-
-        // TODO - -handle remote resource
-        SegmentedAsciiDataSet ds = new SegmentedAsciiDataSet(locator);
-
-        for (String trackName : ds.getDataHeadings()) {
-
-            SegmentedDataSource dataSource = new SegmentedDataSource(trackName, ds);
-            Track track = new DataSourceTrack(locator, trackName, dataSource);
-
-            track.setRendererClass(HeatmapRenderer.class);
-
-            track.setTrackType(ds.getType());
-            newTracks.add(track);
-        }
-    }
-
-    private void loadBinarySegFile(ResourceLocator locator, List<Track> newTracks) {
-
-        SegmentedBinaryDataSet ds = null;
-
-        ds = new SegmentedBinaryDataSet(locator);
-
-        for (String trackName : ds.getSampleNames()) {
-
-            SegmentedDataSource dataSource = new SegmentedDataSource(
-                    trackName, ds);
-            Track track = new DataSourceTrack(locator, trackName, dataSource);
-
-            track.setRendererClass(HeatmapRenderer.class);
-
-            track.setTrackType(ds.getType());
-            newTracks.add(track);
-        }
-    }
-
-
-    // TODO -- implementation
-    private void loadPlinkFile(ResourceLocator locator, List<Track> newTracks) {
-
-        // GWASParser parser = new GWASParser(locator);
-        // GWASData data = parser.parse();
-        // GWASTrack track = new GWASTrack(data);
-        // track.setName(locator.getDisplayName());
-        // newTracks.add(track);
-
-        MessageUtils.showMessage("Plink loading not implemented yet");
-    }
-
-
-    private void loadDASResource(ResourceLocator locator, List<Track> newTracks) {
-
-        //TODO Connect and get all the attributes of the DAS server, and run the appropriate load statements
-        //TODO Currently we are only going to be doing features
-        // TODO -- move the source creation to a factory
-
-        DASSource source = locator.getType() != null && locator.getType().equals("DAS") ?
-                new DASConnection(locator) : new DASFileSource(locator);
-
-        if (source.isCapable("Features")) {
-            loadDASFeatures(locator, newTracks, source);
+            return mainFrame.getDataPanel(FEATURE_PANEL_NAME);
         } else {
-            throw new DataLoadException("Features are not supported by the DAS Server", locator.toString());
-        }
-    }
-
-    private void loadDASFeatures(ResourceLocator locator, List<Track> newTracks, DASSource source) {
-
-        DASFeatureParser featureParser = DASFeatureParser.getInstanceFor(locator.getPath());
-        List<FeatureTrack> dasTracks = featureParser.loadTracks(locator, source);
-        for (FeatureTrack track : dasTracks) {
-
-            String name = locator.getDisplayName();
-            track.setDisplayName(name);
-            track.setTrackType(TrackType.MUTATION);
-            track.setRendererClass(DASFeatureRenderer.class);
-            track.setMinimumHeight(2);
-            track.setHeight(20);
-            newTracks.add(track);
-        }
-    }
-
-    private void regsiterAttributes(Track track, String trackSetName) {
-        track.setAttributeValue("DATA FILE", trackSetName);
-        track.setAttributeValue("DATA TYPE", track.getTrackType().toString());
-        track.setAttributeValue("NAME", track.getId());
-    }
-
-    private void sortByList(List<Track> tracks, List<String> trackIds) {
-
-        final Map<String, Integer> trackPositions = new HashMap();
-        for (int i = 0; i < trackIds.size(); i++) {
-            trackPositions.put(trackIds.get(i), i);
-        }
-        Comparator c = new Comparator<Track>() {
-            public int compare(Track t1, Track t2) {
-                String id1 = t1.getId();
-                int p1 = trackPositions.containsKey(id1) ? trackPositions.get(id1) : Integer.MAX_VALUE;
-                String id2 = t2.getId();
-                int p2 = trackPositions.containsKey(id2) ? trackPositions.get(id2) : Integer.MAX_VALUE;
-                return p1 - p2;
-            }
-        };
-        Collections.sort(tracks, c);
-
-
-    }
-
-    private void sortByAttributes(List<Track> tracks,
-                                  final String attributeNames[],
-                                  final boolean[] ascending) {
-        if ((tracks != null) && !tracks.isEmpty()) {
-            Comparator comparator = new Comparator() {
-
-                public int compare(Object arg0, Object arg1) {
-                    Track t1 = (Track) arg0;
-                    Track t2 = (Track) arg1;
-
-                    for (int i = 0; i <
-                            attributeNames.length; i++) {
-                        String attName = attributeNames[i];
-
-                        if (attName != null) {
-                            String value1 = t1.getAttributeValue(attName);
-
-                            if (value1 == null) {
-                                value1 = "";
-                            }
-
-                            String value2 = t2.getAttributeValue(attName);
-
-                            if (value2 == null) {
-                                value2 = "";
-                            }
-
-                            int c = 0;
-                            int k = 0;
-                            if (value1.matches("[^0-9]") || value2.matches("[^0-9]")) {
-                                c = value1.compareTo(value2);
-                            } else {
-                                boolean numeric = false;
-                                while (k < value1.length() && k < value2.length()) {
-                                    while (k < value1.length() && k < value2.length() && Character.isDigit(value1.charAt(
-                                            k)) && Character.isDigit(value2.charAt(k))) {
-                                        int num1 = Character.getNumericValue(value1.charAt(k));
-                                        int num2 = Character.getNumericValue(value2.charAt(k));
-
-                                        if (c == 0) {
-                                            if (num1 < num2) {
-                                                c = -1;
-                                            }
-
-                                            if (num1 > num2) {
-                                                c = 1;
-                                            }
-
-                                        }
-                                        k++;
-                                        numeric =
-                                                true;
-                                    }
-
-                                    if (numeric && k < value1.length() && Character.isDigit(value1.charAt(
-                                            k))) {
-                                        c = 1;
-                                        numeric =
-                                                false;
-                                    } else if (numeric && k < value2.length() && Character.isDigit(value2.charAt(
-                                            k))) {
-                                        c = -1;
-                                        numeric =
-                                                false;
-                                    }
-
-                                    if (k < value1.length() && k < value2.length() && c == 0) {
-                                        c = Character.valueOf(value1.charAt(k)).compareTo(value2.charAt(
-                                                k));
-                                    }
-
-                                    if (c != 0) {
-                                        break;
-                                    }
-
-                                    k++;
-                                }
-
-                            }
-
-                            if (c == 0 && k < value1.length()) {
-                                c = 1;
-                            }
-
-                            if (c == 0 && k < value2.length()) {
-                                c = -1;
-                            }
-
-                            if (c != 0) {
-                                return (ascending[i] ? c : -c);
-                            }
-
-                        }
-                    }
-
-                    // All compares are equal
-                    return 0;
-                }
-            };
-
-            Collections.sort(tracks, comparator);
+            return mainFrame.getDataPanel(DATA_PANEL_NAME);
         }
-
     }
 
     /**
@@ -1632,212 +605,26 @@ public class TrackManager {
      * @param region
      * @param type
      */
-    public void sortGroup(final RegionOfInterest region,
-                          final RegionScoreType type) {
-
-        boolean useLinkedSorting = PreferenceManager.getInstance().isLinkedSortingEnabled();
-        String linkingAtt = PreferenceManager.getInstance().getOverlayAttribute();
-
-        for (Map.Entry<String, Map<String, TrackGroup>> entry : trackGroups.entrySet()) {
-            String dataPanelName = entry.getKey();
-            Map<String, TrackGroup> groupsMap = entry.getValue();
-
-            if (groupsMap.size() <= 1) {
-                if ((linkingAtt == null) || !useLinkedSorting) {
-                    sortByRegionScore(panelTrackGroups.get(dataPanelName).getTracks(), region, type);
-                } else {
-                    sortGroup(panelTrackGroups.get(dataPanelName), region, linkingAtt, type);
-                }
+    public void sortByRegionScore(RegionOfInterest region,
+                                  RegionScoreType type) {
 
-            } else {
-                List<TrackGroup> dataGroups = new ArrayList();
-                dataGroups.addAll(groupsMap.values());
-                sortGroupsByRegionScore(dataGroups, region, type);
-                groupsMap.clear();
-
-                for (TrackGroup group : dataGroups) {
-                    groupsMap.put(group.getAttributeValue(), group);
-
-                    // If there is a non-null linking attribute
-                    // Segregate tracks into 2 groups, those matching the score type and those that do not
-                    if ((linkingAtt == null) || !useLinkedSorting) {
-                        sortByRegionScore(group.getTracks(), region, type);
-                    } else {
-                        sortGroup(group, region, linkingAtt, type);
-                    }
+        if (region == null) {
+            ViewContext vc = ViewContext.getInstance();
+            String chr = vc.getChrName();
+            int start = (int) vc.getOrigin();
+            int end = (int) vc.getEnd() + 1;
+            region = new RegionOfInterest(chr, start, end, "");
 
-                }
-            }
         }
-    }
-
-    private void sortByRegionScore(List<Track> tracks,
-                                   final RegionOfInterest region,
-                                   final RegionScoreType type) {
-        if ((tracks != null) && (region != null) && !tracks.isEmpty()) {
-            final int zoom = Math.max(0,
-                    IGVModel.getInstance().getViewContext().getZoom());
-            final String chr = region.getChromosomeName();
-            final int start = region.getStart();
-            final int end = region.getEnd();
-
-            Comparator<Track> c = new Comparator<Track>() {
-
-                public int compare(Track t1, Track t2) {
-                    float s1 = t1.getRegionScore(chr, start, end, zoom, type);
-                    float s2 = t2.getRegionScore(chr, start, end, zoom, type);
-
-                    if (s2 > s1) {
-                        return 1;
-                    } else if (s1 < s2) {
-                        return -1;
-                    } else {
-                        return 0;
-                    }
-
-                }
-            };
-
-            Collections.sort(tracks, c);
-
-        }
-
-    }
-
-    private void sortGroup(TrackGroup group, final RegionOfInterest region,
-                           String linkingAtt,
-                           final RegionScoreType type) {
-        List<Track> tracksWithScore = new ArrayList(group.getTracks().size());
-        List<Track> otherTracks = new ArrayList(group.getTracks().size());
-        for (Track t : group.getTracks()) {
-            if (t instanceof DataTrack) {
-                if (((DataTrack) t).isRegionScoreType(type)) {
-                    tracksWithScore.add(t);
-                } else {
-                    otherTracks.add(t);
-                }
-
-            } else {
-                otherTracks.add(t);
-            }
-
-        }
-
-        sortByRegionScore(tracksWithScore, region, type);
-        List<String> sortedAttributes = new ArrayList();
-        for (Track t : tracksWithScore) {
-            String att = t.getAttributeValue(linkingAtt);
-            if (att != null) {
-                sortedAttributes.add(att);
-            }
-
-        }
-        sortByAttributeOrder(otherTracks, sortedAttributes, linkingAtt);
-
-        group.clear();
-        group.addAll(tracksWithScore);
-        group.addAll(otherTracks);
-    }
-
-// TODO -- refactor to elimate this copied method, use interface for
-
-    // things with region scores (tracks & track groups).
-
-    private void sortGroupsByRegionScore(List<TrackGroup> groups,
-                                         final RegionOfInterest region,
-                                         final RegionScoreType type) {
-        if ((groups != null) && (region != null) && !groups.isEmpty()) {
-            final int zoom = Math.max(0,
-                    IGVModel.getInstance().getViewContext().getZoom());
-            final String chr = region.getChromosomeName();
-            final int start = region.getStart();
-            final int end = region.getEnd();
-            Comparator<TrackGroup> c = new Comparator<TrackGroup>() {
-
-                public int compare(TrackGroup t1, TrackGroup t2) {
-                    float s1 = t1.getRegionScore(chr, start, end, zoom, type);
-                    float s2 = t2.getRegionScore(chr, start, end, zoom, type);
-
-                    if (s2 > s1) {
-                        return 1;
-                    } else if (s1 < s2) {
-                        return -1;
-                    } else {
-                        return 0;
-                    }
-
-                }
-            };
-
-            Collections.sort(groups, c);
-        }
-
-    }
-
-    /**
-     * @param tracks
-     */
-    private void sortByAttributeOrder(List<Track> tracks,
-                                      List<String> sortedAttributes,
-                                      final String attributeId) {
-        if ((tracks != null) && (sortedAttributes != null) && !tracks.isEmpty()) {
-
-            // Create a rank hash.  Loop backwards so that the lowest index for an attribute
-            final HashMap<String, Integer> rankMap = new HashMap(
-                    sortedAttributes.size() * 2);
-            for (int i = sortedAttributes.size() - 1; i >=
-                    0; i--) {
-                rankMap.put(sortedAttributes.get(i), i);
-            }
-
-// Comparator for sorting in ascending order
-            Comparator<Track> c = new Comparator<Track>() {
-
-                public int compare(Track t1, Track t2) {
-                    String a1 = t1.getAttributeValue(attributeId);
-                    String a2 = t2.getAttributeValue(attributeId);
-                    Integer r1 = ((a1 == null) ? null : rankMap.get(a1));
-                    Integer r2 = ((a2 == null) ? null : rankMap.get(a2));
-                    if ((r1 == null) && (r2 == null)) {
-                        return 0;
-                    } else if (r1 == null) {
-                        return 1;
-                    } else if (r2 == null) {
-                        return -1;
-                    } else {
-                        return r1.intValue() - r2.intValue();
-                    }
-
-                }
-            };
-
-            Collections.sort(tracks, c);
 
+        for (TrackPanelScrollPane tsp : getTrackPanelScrollPanes()) {
+            TrackPanel tsv = tsp.getTrackPanel();
+            tsv.sortByRegionsScore(region, type);
         }
 
     }
 
     /**
-     * Sort all groups (data and feature) by a computed score over a region.  The
-     * sort is done twice (1) groups are sorted with the featureGroup, and (2) the
-     * groups themselves are sorted.
-     *
-     * @param region
-     * @param type
-     */
-    public void doROC(final RegionOfInterest region,
-                      final RegionScoreType type) {
-    }
-
-    /*
-     *     class LinkedSampleComparator<Track> implements Comparator<Track> {
-     * RegionScoreType scoreType;
-     * String linkingAttribute;
-     * public int compare(Track t1, Track t2) {
-     * }
-     * }
-     */
-    /**
      * Method description
      *
      * @return
@@ -1846,43 +633,6 @@ public class TrackManager {
         return groupByAttribute;
     }
 
-    //  To support mutation track overlay.  Generalize later.  Cancer specific option.
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public boolean isDoOverlays() {
-        return PreferenceManager.getInstance().getOverlayTracks();
-    }
-
-    /**
-     * Return the track type
-     *
-     * @return
-     */
-    public TrackType getOverlayTrackType() {
-        return IGVConstants.overlayTrackType;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public String getOverlayAttribute() {
-        return PreferenceManager.getInstance().getOverlayAttribute();
-    }
-
-    /**
-     * Method description
-     *
-     * @param value
-     * @return
-     */
-    public List<Track> getOverlayTracks(String value) {
-        return overlayTracksMap.get(value);
-    }
 
     /**
      * Method description
@@ -1891,10 +641,9 @@ public class TrackManager {
      * @return
      */
     public List<Track> getOverlayTracks(Track track) {
-        String overlayAttribute = PreferenceManager.getInstance().getOverlayAttribute();
+        String overlayAttribute = IGVMainFrame.getInstance().getSession().getOverlayAttribute();
         String value = track.getAttributeValue(overlayAttribute);
-
-        return getOverlayTracks(value);
+        return overlayTracksMap.get(value);
 
     }
 
@@ -1906,37 +655,39 @@ public class TrackManager {
     public void loadGeneTrack(String genomeId) {
 
         // Load genes
-        geneData = GeneManager.getGeneManager(genomeId);
+        GeneManager geneManager = GeneManager.getGeneManager(genomeId);
 
 
-        /*
-         * if (geneData == null)
-         * {
-         *   return;
-         * }
-         */
-
         Map<String, List<Feature>> featureMap = null;
-        if (geneData != null) {
-            featureMap = geneData.getChromsomeGeneMap();
+        if (geneManager != null) {
+            featureMap = geneManager.getChromsomeGeneMap();
         } else {
             featureMap = new HashMap<String, List<Feature>>();
         }
 
-        FeatureTrack geneFeatureTrack = new FeatureTrack(null, GENE_TRACK_NAME,
-                featureMap);
-
-        geneFeatureTrack.setMinimumHeight(5);
+        FeatureTrack geneFeatureTrack = new FeatureTrack("Genes", new FeatureCollectionSource(featureMap));
+        geneFeatureTrack.setMinimumHeight(30);
         geneFeatureTrack.setHeight(30);
-        geneFeatureTrack.setRendererClass(GeneTrackRenderer.class);
+        geneFeatureTrack.setRendererClass(BasicFeatureRenderer.class);
         geneFeatureTrack.setColor(Color.BLUE.darker());
 
         SequenceTrack seqTrack = new SequenceTrack("Reference");
-        if (geneData != null) {
-            String geneTrackName = geneData.getGeneTrackName();
+        if (geneManager != null) {
+
+            if (geneManager.getTrackProperties() != null) {
+                geneFeatureTrack.setTrackProperties(geneManager.getTrackProperties());
+            }
+
+            String geneTrackName = geneManager.getGeneTrackName();
 
             GeneTrack gt = new GeneTrack(geneFeatureTrack, seqTrack);
-            gt.setDisplayName(geneTrackName);
+            gt.setName(geneTrackName);
+            gt.setHeight(45);
+
+            Genome genome = GenomeManager.getInstance().getGenome(genomeId);
+            if (genome != null && gt.getUrl() == null) {
+                gt.setUrl(genome.getAnnotationURL());
+            }
 
             setGeneTrack(gt);
         } else {
@@ -1946,83 +697,34 @@ public class TrackManager {
     }
 
     /**
-     * Method description
-     *
-     * @return
-     */
-    public GeneManager getGeneManager() {
-        return geneData;
-    }
-
-    /**
-     * Method description
+     * Replace current gene track with new one.  This is called upon switching genomes
      *
-     * @param id
-     * @param match
-     * @return
+     * @param newGeneTrack
      */
-    public synchronized List<Track> findTracksById(String id, MatchTrack match) {
-
-        List<Track> tracks = new ArrayList();
+    private void setGeneTrack(Track newGeneTrack) {
 
-        if (id != null) {
-            for (TrackGroup tg : panelTrackGroups.values()) {
-                List<Track> dataTracks = tg.getTracks();
-
-                for (Track track : dataTracks) {
-                    if (track != null) {
-                        if (MatchTrack.EXACT.equals(match)) {
-                            if (track.getId().equalsIgnoreCase(id)) {
-                                tracks.add(track);
-                            }
-
-                        } else if (MatchTrack.CONTAINS.equals(match)) {
-                            if (track.getId().toLowerCase().indexOf(id.toLowerCase()) != -1) {
-                                tracks.add(track);
-                            }
-
-                        }
-                    }
-                }
+        boolean foundGeneTrack = false;
+        for (TrackPanelScrollPane tsp : getTrackPanelScrollPanes()) {
+            TrackPanel tsv = tsp.getTrackPanel();
+            foundGeneTrack = tsv.setGeneTrack(geneTrack, newGeneTrack);
+            if (foundGeneTrack) {
+                break;
             }
         }
 
-        return tracks;
-    }
-
-    /**
-     * Method description
-     */
-    public void refreshTrackData() {
-        long timestamp = System.currentTimeMillis();
-
-        for (Track track : this.getAllTracks(false)) {
-            track.refreshData(timestamp);
+        if (!foundGeneTrack) {
+            if (PreferenceManager.getInstance().isShowSingleTrackPane()) {
+                mainFrame.getDataPanel(DATA_PANEL_NAME).addTrack(newGeneTrack);
+            } else {
+                mainFrame.getDataPanel(FEATURE_PANEL_NAME).addTrack(newGeneTrack);
+            }
         }
 
-    }
-
-    /**
-     * Return the set of DataResourceLocators for all loaded tracks..
-     * Used to test if a track has been previously loaded.
-     * This set could be maintained as tracks are loaded and removed to avoid
-     * the need to compute it on the fly, but this could be error prone.
-     *
-     * @return
-     */
-    public Set<ResourceLocator> getLocatorSet() {
-        HashSet<ResourceLocator> locators = new HashSet();
-
-        for (Track track : getAllTracks(false)) {
-            locators.add(track.getResourceLocator());
-        }
+        // Keep a reference to this track so it can be removed
+        geneTrack = newGeneTrack;
 
-        return locators;
     }
 
-    public void setRestoringSession(boolean restoringSession) {
-        this.restoringSession = restoringSession;
-    }
 
     /*
   public void updateTrackPositions(String panelName) {
@@ -2031,7 +733,7 @@ public class TrackManager {
   for (TrackGroup group : groups) {
   if (group.isVisible()) {
   if (groups.size() > 1) {
-  regionY += IGVConstants.groupGap;
+  regionY += UIStringConstants.groupGap;
   }
   int previousRegion = -1;
   for (Track track : group.getTracks()) {
@@ -2082,8 +784,8 @@ public class TrackManager {
     public void dumpData() {
     PrintWriter pw = null;
     try {
-    String chr = IGVModel.getInstance().getViewContext().getChromosome().getName();
-    double x = IGVModel.getInstance().getViewContext().getOrigin();
+    String chr = ViewContext.getInstance().getChromosome().getName();
+    double x = ViewContext.getInstance().getOrigin();
     pw = new PrintWriter(new FileWriter("DataDump.tab"));
     pw.println("Sample\tCopy Number\tExpression\tMethylation");
     for (String name : groupsMap.keySet()) {
@@ -2129,7 +831,7 @@ public class TrackManager {
     x);
     if (score != null) {
     pw.println(
-    track.getDisplayName() + "\t" + track.getTrackType() + "\t" + score.getScore());
+    track.getName() + "\t" + track.getTrackType() + "\t" + score.getScore());
     }
     }
     }
@@ -2139,22 +841,6 @@ public class TrackManager {
     pw.close();
     }
     }     * */
-    /**
-     * Utility class for creating tracks from and HDF5 file.
-     */
-    static class HDF5TrackReader {
-
-        ResourceLocator locator;
-        HDFDataManager dataManager;
-
-        /**
-         * Constructs ...
-         *
-         * @param locator
-         */
-        public HDF5TrackReader(ResourceLocator locator) {
-            this.locator = locator;
-            dataManager = HDFDataManagerFactory.getDataManager(locator);
-        }
-    }
+
+
 }
diff --git a/src/org/broad/igv/track/TrackMenuUtils.java b/src/org/broad/igv/track/TrackMenuUtils.java
index e8230ea..8882673 100644
--- a/src/org/broad/igv/track/TrackMenuUtils.java
+++ b/src/org/broad/igv/track/TrackMenuUtils.java
@@ -1,37 +1,47 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
+* Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+* All Rights Reserved.
+*
+* This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+* is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+*
+* THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+* ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+* OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+* RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+* ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+* DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+* BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+* FOREGOING.
+*/
+
 package org.broad.igv.track;
 
 import com.jidesoft.swing.JidePopupMenu;
 import org.apache.commons.math.stat.StatUtils;
 import org.apache.log4j.Logger;
 import org.broad.igv.PreferenceManager;
+import org.broad.igv.feature.Exon;
+import org.broad.igv.feature.Feature;
+import org.broad.igv.feature.SequenceManager;
 import org.broad.igv.renderer.*;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.ui.DataRangeDialog;
-import org.broad.igv.ui.GuiUtilities;
+import org.broad.igv.ui.FontManager;
 import org.broad.igv.ui.HeatmapScaleDialog;
 import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.util.MessageUtils;
 import org.broad.igv.ui.util.UIUtilities;
 
 import javax.swing.*;
 import java.awt.*;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
@@ -44,76 +54,87 @@ public class TrackMenuUtils {
     static Logger log = Logger.getLogger(TrackMenuUtils.class);
     final static String LEADING_HEADING_SPACER = "  ";
 
+
     /**
      * Return a popup menu with items applicable to the collection of tracks.
      *
      * @param tracks
      * @return
      */
-    public static JPopupMenu getPopupMenu(Collection<Track> tracks, int x, int y, String title) {
+    public static JPopupMenu getPopupMenu(Collection<Track> tracks, String title, MouseEvent e) {
 
         if (log.isDebugEnabled()) {
             log.debug("enter getPopupMenu");
         }
 
+        JidePopupMenu menu = getEmptyPopup(title);
+        addStandardItems(menu, tracks, e);
+        return menu;
+
+    }
+
+    public static JidePopupMenu getEmptyPopup(String title) {
+        JidePopupMenu menu = new JidePopupMenu();
+
+        JLabel popupTitle = new JLabel(LEADING_HEADING_SPACER + title, JLabel.CENTER);
+        Font newFont = FontManager.getScalableFont(Font.BOLD, 12);
+        popupTitle.setFont(newFont);
+        if (popupTitle != null) {
+            menu.add(popupTitle);
+            menu.addSeparator();
+        }
+        return menu;
+    }
+
+    public static void addStandardItems(JPopupMenu menu, Collection<Track> tracks, MouseEvent e) {
         boolean hasDataTracks = false;
         boolean hasFeatureTracks = false;
         for (Track track : tracks) {
 
-            // This is a messy test. A "mutation track" is implemented as a
-            // FeatureTrack, but the "DataTrack" menu applies.  Refactor later
-            // to fix this,  for now live with the messy test.
-            if ((track instanceof DataTrack) || (track instanceof HDFDataTrack) || ((track instanceof FeatureTrack) && ((FeatureTrack) track).isMutationTrack())) {
+            //  TODO -- this is ugly, refactor to remove instanceof
+            if ((track instanceof DataTrack) || (track instanceof HDFDataTrack)) {
                 hasDataTracks = true;
-            } else if (track instanceof FeatureTrack) {
+            } else if (track instanceof FeatureTrack || track instanceof GeneTrack) {
                 hasFeatureTracks = true;
             }
             if (hasDataTracks && hasFeatureTracks) {
-                return getSharedPopupMenu();
+                break;
             }
         }
 
-        if (hasDataTracks) {
-            return getDataPopupMenu(tracks, x, y, title);
+        if (hasDataTracks && hasFeatureTracks) {
+            addSharedItems(menu, tracks);
+        } else if (hasDataTracks) {
+            addDataItems(menu, tracks);
         } else {
-            return getFeaturePopupMenu(tracks, x, y, title);
+            addFeatureItems(menu, tracks, e, hasFeatureTracks);
         }
-
     }
 
+
     /**
      * Return popup menu with items applicable to data tracks
      *
      * @return
      */
-    public static JPopupMenu getDataPopupMenu(final Collection<Track> tracks, int x, int y, String title) {
+    private static void addDataItems(JPopupMenu menu, final Collection<Track> tracks) {
 
         if (log.isDebugEnabled()) {
             log.debug("enter getDataPopupMenu");
         }
 
-        JidePopupMenu dataPopupMenu = new JidePopupMenu();
-
         final String[] labels = {"Heatmap", "Bar Chart", "Scatterplot", "Line Plot"};
         final Class[] renderers = {HeatmapRenderer.class, BarChartRenderer.class,
                 ScatterplotRenderer.class, LineplotRenderer.class
         };
 
-        JLabel popupTitle = new JLabel(LEADING_HEADING_SPACER + title,
-                JLabel.CENTER);
+        //JLabel popupTitle = new JLabel(LEADING_HEADING_SPACER + title, JLabel.CENTER);
 
-        Font newFont = dataPopupMenu.getFont().deriveFont(Font.BOLD, 12);
-        popupTitle.setFont(newFont);
-        if (popupTitle != null) {
-            dataPopupMenu.add(popupTitle);
-            dataPopupMenu.addSeparator();
-        }
-
-        JLabel rendererHeading = new JLabel(
-                LEADING_HEADING_SPACER + "Type of Graph", JLabel.LEFT);
+        Font newFont = FontManager.getScalableFont(Font.BOLD, 12);
+        JLabel rendererHeading = new JLabel(LEADING_HEADING_SPACER + "Type of Graph", JLabel.LEFT);
         rendererHeading.setFont(newFont);
 
-        dataPopupMenu.add(rendererHeading);
+        menu.add(rendererHeading);
 
         // Get existing selections
         Set<Class> currentRenderers = new HashSet<Class>();
@@ -123,7 +144,7 @@ public class TrackMenuUtils {
             }
         }
 
-        // Create and add a menu item
+        // Create and renderer menu items
         for (int i = 0; i < labels.length; i++) {
             JCheckBoxMenuItem item = new JCheckBoxMenuItem(labels[i]);
             final Class rendererClass = renderers[i];
@@ -133,12 +154,13 @@ public class TrackMenuUtils {
             item.addActionListener(new ActionListener() {
 
                 public void actionPerformed(ActionEvent e) {
-                    changeRenderer(rendererClass);
+                    changeRenderer(tracks, rendererClass);
+                    clearTrackSelections();
                 }
             });
-            dataPopupMenu.add(item);
+            menu.add(item);
         }
-        dataPopupMenu.addSeparator();
+        menu.addSeparator();
 
 
         // Get union of all valid window functions for selected tracks
@@ -146,10 +168,16 @@ public class TrackMenuUtils {
         for (Track track : tracks) {
             avaibleWindowFunctions.addAll(track.getAvailableWindowFunctions());
         }
+
         final WindowFunction[] orderedWindowFunctions = {
-                // WindowFunction.min,
-                WindowFunction.percentile10, WindowFunction.median, WindowFunction.mean,
-                WindowFunction.percentile90, WindowFunction.max
+                WindowFunction.min,
+                WindowFunction.percentile2,
+                WindowFunction.percentile10,
+                WindowFunction.median,
+                WindowFunction.mean,
+                WindowFunction.percentile90,
+                WindowFunction.percentile98,
+                WindowFunction.max
         };
 
 
@@ -163,12 +191,10 @@ public class TrackMenuUtils {
         }
 
         if (!avaibleWindowFunctions.isEmpty() || !currentWindowFunctions.isEmpty()) {
-            JLabel statisticsHeading = new JLabel(
-                    LEADING_HEADING_SPACER + "Windowing Function",
-                    JLabel.LEFT);
+            JLabel statisticsHeading = new JLabel(LEADING_HEADING_SPACER + "Windowing Function", JLabel.LEFT);
             statisticsHeading.setFont(newFont);
 
-            dataPopupMenu.add(statisticsHeading);
+            menu.add(statisticsHeading);
 
             for (final WindowFunction wf : orderedWindowFunctions) {
                 JCheckBoxMenuItem item = new JCheckBoxMenuItem(
@@ -181,32 +207,41 @@ public class TrackMenuUtils {
                     item.addActionListener(new ActionListener() {
 
                         public void actionPerformed(ActionEvent e) {
-                            changeStatType(wf.toString());
+                            changeStatType(wf.toString(), tracks);
+                            clearTrackSelections();
                         }
                     });
-                    dataPopupMenu.add(item);
+                    menu.add(item);
                 }
             }
-            dataPopupMenu.addSeparator();
+            menu.addSeparator();
         }
 
+        JLabel scaleHeading = new JLabel(LEADING_HEADING_SPACER + "Data Range", JLabel.LEFT);
+        scaleHeading.setFont(newFont);
+        menu.add(scaleHeading);
 
-        JLabel trackSettingsHeading = new JLabel(
-                LEADING_HEADING_SPACER + "Track Settings",
-                JLabel.LEFT);
-        trackSettingsHeading.setFont(newFont);
+        menu.add(getDataRangeItem(tracks));
+
+        if (tracks.size() > 0) {
+            menu.add(getLogScaleItem(tracks));
+        }
+
+        menu.add(getAutoscaleItem(tracks));
 
-        dataPopupMenu.add(trackSettingsHeading);
+        menu.add(getHeatmapScaleItem(tracks));
 
-        addTrackRenameItem(dataPopupMenu);
+        menu.add(getShowDataRangeItem(tracks));
+
+        JLabel trackSettingsHeading = new JLabel(LEADING_HEADING_SPACER + "Track Settings", JLabel.LEFT);
+        trackSettingsHeading.setFont(newFont);
 
-        addDataRangeItem(dataPopupMenu, tracks, false);
+        menu.add(trackSettingsHeading);
 
-        addHeatmapItem(dataPopupMenu, tracks);
+        menu.add(getTrackRenameItem(tracks));
 
-        addTrackSettingsMenuItems(dataPopupMenu);
+        addTrackSettingsMenuItems(menu, false, tracks);
 
-        return dataPopupMenu;
     }
 
     /**
@@ -214,36 +249,63 @@ public class TrackMenuUtils {
      *
      * @return
      */
-    public static JPopupMenu getFeaturePopupMenu(Collection<Track> tracks, int x, int y, String title) {
+    private static void addFeatureItems(JPopupMenu featurePopupMenu, final Collection<Track> tracks,
+                                        MouseEvent e, boolean hasFeatureTracks) {
 
-        JPopupMenu featurePopupMenu = new JidePopupMenu();
-
-        JLabel popupTitle = new JLabel(LEADING_HEADING_SPACER + title,
-                JLabel.CENTER);
 
         Font newFont = featurePopupMenu.getFont().deriveFont(Font.BOLD, 12);
-        popupTitle.setFont(newFont);
-        if (popupTitle != null) {
-            featurePopupMenu.add(popupTitle);
-            featurePopupMenu.addSeparator();
-        }
+        // popupTitle.setFont(newFont);
+        // if (popupTitle != null) {
+        //     featurePopupMenu.add(popupTitle);
+        //     featurePopupMenu.addSeparator();
+        // }
+
 
+        JLabel trackSettingsHeading = new JLabel(LEADING_HEADING_SPACER + "Track Settings", JLabel.LEFT);
 
-        JLabel trackSettingsHeading = new JLabel(
-                LEADING_HEADING_SPACER + "Track Settings",
-                JLabel.LEFT);
         trackSettingsHeading.setFont(newFont);
 
         featurePopupMenu.add(trackSettingsHeading);
 
-        addTrackRenameItem(featurePopupMenu);
+        featurePopupMenu.add(getTrackRenameItem(tracks));
+
+        featurePopupMenu.add(getExpandCollapseItem(tracks));
+
+        if (hasFeatureTracks) {
+            featurePopupMenu.add(getChangeFontSizeItem(tracks));
+
+            if (tracks.size() == 1) {
+                Track t = tracks.iterator().next();
+                Feature f = t.getFeatureAtMousePosition(e);
+                if (f != null) {
+                    featurePopupMenu.addSeparator();
+                    featurePopupMenu.add(getCopyDetailsItem(f, e.getX()));
+
+                    // If we are over an exon, copy its sequence instead of the entire feature.
+                    double position = ViewContext.getInstance().getChromosomePosition(e.getX());
+                    Collection<Exon> exons = f.getExons();
+                    if (exons != null) {
+                        for (Exon exon : exons) {
+                            if (position > exon.getStart() && position < exon.getEnd()) {
+                                f = exon;
+                                break;
+                            }
+                        }
+                    }
+
+                    featurePopupMenu.add(getCopySequenceItem(f));
 
-        addExpandCollapseItem(featurePopupMenu);
+                    f.getExons();
 
-        addTrackSettingsMenuItems(featurePopupMenu, true);
+                }
+            }
 
+            featurePopupMenu.addSeparator();
+            featurePopupMenu.add(getChangeFeatureWindow(tracks));
+            featurePopupMenu.addSeparator();
+        }
 
-        return featurePopupMenu;
+        addTrackSettingsMenuItems(featurePopupMenu, true, tracks);
     }
 
     /**
@@ -251,27 +313,16 @@ public class TrackMenuUtils {
      *
      * @return
      */
-    public static JPopupMenu getSharedPopupMenu() {
-
-        JPopupMenu sharedPopupMenu = new JidePopupMenu();
-
-        JLabel trackSettingsHeading = new JLabel(
-                LEADING_HEADING_SPACER + "Track Settings",
-                JLabel.LEFT);
-        sharedPopupMenu.add(trackSettingsHeading);
+    private static void addSharedItems(JPopupMenu menu, final Collection<Track> tracks) {
 
-        addTrackSettingsMenuItems(sharedPopupMenu);
+        JLabel trackSettingsHeading = new JLabel(LEADING_HEADING_SPACER + "Track Settings", JLabel.LEFT);
+        menu.add(trackSettingsHeading);
+        addTrackSettingsMenuItems(menu, false, tracks);
 
-
-        return sharedPopupMenu;
     }
 
-    public static void addTrackSettingsMenuItems(JPopupMenu menu) {
-        addTrackSettingsMenuItems(menu, false);
-    }
 
-    public static void addTrackSettingsMenuItems(JPopupMenu menu,
-                                                 boolean featureTracksOnly) {
+    private static void addTrackSettingsMenuItems(JPopupMenu menu, boolean featureTracksOnly, final Collection<Track> tracks) {
         // Change track color by attribute
         String colorLabel = featureTracksOnly
                 ? "Change Track Color" : "Change Track Color (Positive Values)";
@@ -279,12 +330,8 @@ public class TrackMenuUtils {
         item.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
-
-                    public void run() {
-                        changeTrackColor();
-                    }
-                });
+                changeTrackColor(tracks);
+                clearTrackSelections();
             }
         });
         menu.add(item);
@@ -298,10 +345,11 @@ public class TrackMenuUtils {
             item.addActionListener(new ActionListener() {
 
                 public void actionPerformed(ActionEvent e) {
-                    GuiUtilities.invokeOnEventThread(new Runnable() {
+                    UIUtilities.invokeOnEventThread(new Runnable() {
 
                         public void run() {
-                            changeAltTrackColor();
+                            changeAltTrackColor(tracks);
+                            clearTrackSelections();
                         }
                     });
                 }
@@ -309,58 +357,47 @@ public class TrackMenuUtils {
             menu.add(item);
         }
 
-        addChangeTrackHeightItem(menu);
-
-
-        // Remove tracks by attribute
-        item = new JMenuItem("Remove Tracks");
-        item.addActionListener(new ActionListener() {
-
-            public void actionPerformed(ActionEvent e) {
-
-                GuiUtilities.invokeOnEventThread(new Runnable() {
+        menu.add(getChangeTrackHeightItem(tracks));
 
-                    public void run() {
-                        removeTracks();
-                    }
-                });
-            }
-        });
-        menu.add(item);
+        menu.add(getRemoveMenuItem(tracks));
     }
 
-    public static void changeStatType(String statType) {
-        for (Track track : getSelectedTracks()) {
+
+    private static void changeStatType(String statType, Collection<Track> selectedTracks) {
+        for (Track track : selectedTracks) {
             track.setStatType(WindowFunction.valueOf(statType));
         }
+        clearTrackSelections();
         refresh();
     }
 
-    public static void addTrackRenameItem(JPopupMenu menu) {
+
+    public static JMenuItem getTrackRenameItem(final Collection<Track> selectedTracks) {
         // Change track height by attribute
         JMenuItem item = new JMenuItem("Rename Track");
         item.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
+                UIUtilities.invokeOnEventThread(new Runnable() {
 
                     public void run() {
-                        renameTrack();
+                        renameTrack(selectedTracks);
+                        clearTrackSelections();
                     }
                 });
             }
         });
-        menu.add(item);
-        if (getSelectedTracks().size() > 1) {
+        if (selectedTracks.size() > 1) {
             item.setEnabled(false);
         }
+        return item;
     }
 
-    public static void addHeatmapItem(JPopupMenu menu, final Collection<Track> selectedTracks) {
+    private static JMenuItem getHeatmapScaleItem(final Collection<Track> selectedTracks) {
 
-        JMenuItem maxValItem = new JMenuItem("Set Heatmap Scale...");
+        JMenuItem item = new JMenuItem("Set Heatmap Scale...");
 
-        maxValItem.addActionListener(new ActionListener() {
+        item.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
                 if (selectedTracks.size() > 0) {
@@ -376,59 +413,163 @@ public class TrackMenuUtils {
                         for (Track track : selectedTracks) {
                             track.setColorScale(colorScale);
                         }
-                        IGVMainFrame.getInstance().clearImageCacheWithNoRepaint();
                         IGVMainFrame.getInstance().repaint();
                     }
 
                 }
+                clearTrackSelections();
 
             }
         });
-        menu.add(maxValItem);
+        return item;
     }
 
-    public static JMenuItem addDataRangeItem(JPopupMenu menu, final Collection<Track> selectedTracks,
-                                             boolean hideMid) {
-        JMenuItem maxValItem = new JMenuItem("Set Data Range...");
+    public static JMenuItem getDataRangeItem(final Collection<Track> selectedTracks) {
+        JMenuItem item = new JMenuItem("Set Data Range...");
 
-        maxValItem.addActionListener(new ActionListener() {
+        item.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
                 if (selectedTracks.size() > 0) {
-                    DataRange prevAxisDefinition = selectedTracks.iterator().next().getDataRange();
-                    DataRangeDialog dlg = new DataRangeDialog(
-                            IGVMainFrame.getInstance(),
-                            prevAxisDefinition);
-                    dlg.setHideMid(true);
+                    // Create a datarange that spans the extent of prev tracks range
+                    float mid = 0;
+                    float min = Float.MAX_VALUE;
+                    float max = Float.MIN_VALUE;
+                    boolean drawBaseline = false;
+                    for (Track t : selectedTracks) {
+                        DataRange dr = t.getDataRange();
+                        min = Math.min(min, dr.getMinimum());
+                        max = Math.max(max, dr.getMaximum());
+                        mid += dr.getBaseline();
+                    }
+                    mid /= selectedTracks.size();
+                    if (mid < min) {
+                        mid = min;
+                    } else if (mid > max) {
+                        min = max;
+                    }
+
+                    DataRange prevAxisDefinition = new DataRange(min, mid, max, drawBaseline);
+                    DataRangeDialog dlg = new DataRangeDialog(IGVMainFrame.getInstance(), prevAxisDefinition);
                     dlg.setVisible(true);
                     if (!dlg.isCanceled()) {
-                        float min = Math.min(dlg.getMin(), dlg.getMax());
-                        float max = Math.max(dlg.getMin(), dlg.getMax());
-                        float mid = dlg.getBase();
-                        if (mid < min) mid = min;
-                        else if (mid > max) mid = max;
-                        DataRange dataRange = new DataRange(min, mid, max);
+                        min = Math.min(dlg.getMax(), dlg.getMin());
+                        max = Math.max(dlg.getMin(), dlg.getMax());
+                        mid = dlg.getBase();
+                        mid = Math.max(min, Math.min(mid, max));
+
+                        DataRange axisDefinition = new DataRange(dlg.getMin(), dlg.getBase(), dlg.getMax());
 
-                        // dlg.isFlipAxis());
                         for (Track track : selectedTracks) {
-                            track.setDataRange(dataRange);
+                            track.setDataRange(axisDefinition);
+                            if (track instanceof DataTrack) {
+                                ((DataTrack) track).setAutoscale(false);
+                            }
                         }
-                        IGVMainFrame.getInstance().clearImageCacheWithNoRepaint();
                         IGVMainFrame.getInstance().repaint();
                     }
 
                 }
+                clearTrackSelections();
+            }
+        });
+        return item;
+    }
 
+    private static JMenuItem getLogScaleItem(final Collection<Track> selectedTracks) {
+        // Change track height by attribute
+
+
+        final JCheckBoxMenuItem logScaleItem = new JCheckBoxMenuItem("Log scale");
+        final boolean logScale = selectedTracks.iterator().next().getDataRange().isLog();
+        logScaleItem.setSelected(logScale);
+        logScaleItem.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                DataRange.Type scaleType = logScaleItem.isSelected() ?
+                        DataRange.Type.LOG :
+                        DataRange.Type.LINEAR;
+                for (Track t : selectedTracks) {
+                    t.getDataRange().setType(scaleType);
+                }
+                clearTrackSelections();
+                IGVMainFrame.getInstance().repaintDataPanels();
             }
         });
-        menu.add(maxValItem);
 
-        return maxValItem;
+        return logScaleItem;
+    }
+
+    private static JMenuItem getAutoscaleItem(final Collection<Track> selectedTracks) {
+
+        final JCheckBoxMenuItem autoscaleItem = new JCheckBoxMenuItem("Autoscale");
+        if (selectedTracks.size() == 0) {
+            autoscaleItem.setEnabled(false);
+
+        } else {
+            boolean autoScale = false;
+            for (Track t : selectedTracks) {
+                if (t instanceof DataTrack && ((DataTrack) t).isAutoscale()) {
+                    autoScale = true;
+                    break;
+                }
+            }
+
+            autoscaleItem.setSelected(autoScale);
+            autoscaleItem.addActionListener(new ActionListener() {
+
+                public void actionPerformed(ActionEvent e) {
+
+                    boolean autoScale = autoscaleItem.isSelected();
+                    for (Track t : selectedTracks) {
+                        if (t instanceof DataTrack) {
+                            ((DataTrack) t).setAutoscale(autoScale);
+                        }
+                    }
+                    clearTrackSelections();
+                    IGVMainFrame.getInstance().repaintDataPanels();
+                }
+            });
+        }
+        return autoscaleItem;
+    }
+
+    private static JMenuItem getShowDataRangeItem(final Collection<Track> selectedTracks) {
+
+        final JCheckBoxMenuItem item = new JCheckBoxMenuItem("Show Data Range");
+        if (selectedTracks.size() == 0) {
+            item.setEnabled(false);
+
+        } else {
+            boolean showDataRange = true;
+            for (Track t : selectedTracks) {
+                if (!t.isShowDataRange()) {
+                    showDataRange = false;
+                    break;
+                }
+            }
+
+            item.setSelected(showDataRange);
+            item.addActionListener(new ActionListener() {
+
+                public void actionPerformed(ActionEvent e) {
+
+                    boolean showDataRange = item.isSelected();
+                    for (Track t : selectedTracks) {
+                        if (t instanceof DataTrack) {
+                            ((DataTrack) t).setShowDataRange(showDataRange);
+                        }
+                    }
+                    clearTrackSelections();
+                    IGVMainFrame.getInstance().repaintDataPanels();
+                }
+            });
+        }
+        return item;
     }
 
-    public static void addExpandCollapseItem(JPopupMenu featurePopupMenu) {
 
-        Collection<Track> tracks = getSelectedTracks();
+    public static JMenuItem getExpandCollapseItem(final Collection<Track> tracks) {
 
         // If any tracks are expanded show the "Collapse" option, otherwise expand
         boolean expanded = false;
@@ -448,55 +589,78 @@ public class TrackMenuUtils {
         item.addActionListener(new ActionListener() {
 
             public void actionPerformed(final ActionEvent e) {
-
-                GuiUtilities.invokeOnEventThread(new Runnable() {
-
-                    public void run() {
-                        doToggleSelectedMultiTrackFeatures();
-                    }
-                });
+                doToggleSelectedMultiTrackFeatures(tracks);
+                clearTrackSelections();
             }
         });
-        featurePopupMenu.add(item);
+
+        return item;
     }
 
-    public static void addRemoveMenuItem(JPopupMenu menu) {
+    public static JMenuItem getRemoveMenuItem(final Collection<Track> selectedTracks) {
 
-        boolean multiple = getSelectedTracks().size() > 1;
+        boolean multiple = selectedTracks.size() > 1;
 
         JMenuItem item = new JMenuItem("Remove Track" + (multiple ? "s" : ""));
         item.addActionListener(new ActionListener() {
 
+
             public void actionPerformed(ActionEvent e) {
 
-                GuiUtilities.invokeOnEventThread(new Runnable() {
+                if (selectedTracks.isEmpty()) {
+                    return;
+                }
 
-                    public void run() {
-                        TrackMenuUtils.removeTracks();
-                    }
-                });
+                StringBuffer buffer = new StringBuffer();
+                for (Track track : selectedTracks) {
+                    buffer.append("\n\t");
+                    buffer.append(track.getName());
+                }
+                String deleteItems = buffer.toString();
+
+                JTextArea textArea = new JTextArea();
+                textArea.setEditable(false);
+                JScrollPane scrollPane = new JScrollPane(textArea);
+                textArea.setText(deleteItems);
+
+                JOptionPane optionPane = new JOptionPane(scrollPane,
+                        JOptionPane.PLAIN_MESSAGE,
+                        JOptionPane.YES_NO_OPTION);
+                optionPane.setPreferredSize(new Dimension(550, 500));
+                JDialog dialog = optionPane.createDialog(IGVMainFrame.getInstance(),
+                        "Remove The Following Tracks");
+                dialog.setVisible(true);
+
+                Object choice = optionPane.getValue();
+                if ((choice != null) && (JOptionPane.YES_OPTION == ((Integer) choice).intValue())) {
+
+
+                    IGVMainFrame.getInstance().getTrackManager().removeTracks(selectedTracks);
+                    IGVMainFrame.getInstance().doResizeTrackPanels();
+                    IGVMainFrame.getInstance().doRefresh();
+                }
+
+                clearTrackSelections();
             }
         });
-        menu.add(item);
+        return item;
     }
 
     /**
      * Toggle selected multi-level tracks
      */
-    public static void doToggleSelectedMultiTrackFeatures() {
+    public static void doToggleSelectedMultiTrackFeatures(final Collection<Track> selectedTracks) {
 
-        for (Track track : getSelectedTracks()) {
+        for (Track track : selectedTracks) {
             boolean isMultiLevel = track.isExpanded();
             track.setExpanded(!isMultiLevel);
         }
 
-        IGVMainFrame.getInstance().clearImageCacheWithNoRepaint();
         IGVMainFrame.getInstance().doResizeTrackPanels();
-        //IGVMainFrame.getInstance().refreshFeatureTrackView();
     }
 
-    public static void changeRenderer(Class rendererClass) {
-        for (Track track : getSelectedTracks()) {
+    public static void changeRenderer(final Collection<Track> selectedTracks, Class rendererClass) {
+        for (Track track : selectedTracks) {
 
             // TODO -- a temporary hack to facilitate RNAi development
             if (track.getTrackType() == TrackType.RNAI) {
@@ -507,134 +671,123 @@ public class TrackMenuUtils {
             }
             track.setRendererClass(rendererClass);
         }
+        clearTrackSelections();
         refresh();
     }
 
-    public static void addTrackRenameItem(JPopupMenu menu, Collection<Track> selectedTracks) {
-        // Change track height by attribute
-        JMenuItem item = new JMenuItem("Rename Track");
-        item.addActionListener(new ActionListener() {
 
-            public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
+    public static void renameTrack(final Collection<Track> selectedTracks) {
 
-                    public void run() {
-                        renameTrack();
-                    }
-                });
-            }
-        });
-        menu.add(item);
-        if (selectedTracks.size() > 1) {
-            item.setEnabled(false);
+        if (selectedTracks.isEmpty()) {
+            return;
+        }
+        Track t = selectedTracks.iterator().next();
+        String newName = JOptionPane.showInputDialog(IGVMainFrame.getInstance(), "Enter new name: ", t.getName());
+
+        if (newName == null || newName.trim() == "") {
+            return;
         }
 
+        t.setName(newName);
+        refresh();
     }
 
-    public static void renameTrack() {
+    public static void changeTrackHeight(final Collection<Track> selectedTracks) {
+
 
-        Collection<Track> selectedTracks = getSelectedTracks();
         if (selectedTracks.isEmpty()) {
             return;
         }
 
-        Track t = getSelectedTracks().iterator().next();
+        final String parameter = "Track height";
+        int value = getIntValue(parameter, getRepresentativeTrackHeight(selectedTracks));
+        if (value == Integer.MIN_VALUE) {
+            return;
+        }
+        value = Math.max(0, value);
 
+        for (Track track : selectedTracks) {
 
-        String newName = JOptionPane.showInputDialog(
-                IGVMainFrame.getInstance(), "Enter new name: ", t.getDisplayName());
+            track.setHeight(value);
 
-        if (newName == null || newName.trim() == "") {
-            return;
         }
 
-        t.setDisplayName(newName);
-
         refresh();
     }
 
-    public static void changeTrackHeight() {
-
+    public static void changeFeatureVisibilityWindow(final Collection<Track> selectedTracks) {
 
-        HashSet<Track> selectedTracks = new HashSet<Track>(getSelectedTracks());
+        Collection<FeatureTrack> featureTracks = new ArrayList(selectedTracks.size());
+        for (Track t : selectedTracks) {
+            if (t instanceof FeatureTrack) {
+                featureTracks.add((FeatureTrack) t);
+            }
+        }
 
-        if (selectedTracks.isEmpty()) {
+        if (featureTracks.isEmpty()) {
             return;
         }
 
-        int intHeight = getRepresentativeTrackHeight(selectedTracks);
-
-        while (true) {
-
-            String height = JOptionPane.showInputDialog(
-                    IGVMainFrame.getInstance(), "Track height: ",
-                    String.valueOf(intHeight));
-
-            if ((height == null) || height.trim().equals("")) {
-                return;
-            }
 
-            try {
-                intHeight = Integer.parseInt(height);
-                break;
-            } catch (NumberFormatException numberFormatException) {
-                JOptionPane.showMessageDialog(IGVMainFrame.getInstance(),
-                        "Track height must be an integer number.");
-            }
+        int origValue = featureTracks.iterator().next().getVisibilityWindow();
+        int origValueKB = Math.max(1, origValue / 1000);
+        int value = getIntValue("Visibility window (kb)", origValueKB);
+        if (value == Integer.MIN_VALUE) {
+            return;
         }
 
-        for (Track track : selectedTracks) {
-            if (intHeight > track.getMinimumHeight())
-                track.setHeight(intHeight);
-            else
-                track.setHeight(track.getPreferredHeight());
+        for (FeatureTrack track : featureTracks) {
+            track.setVisibilityWindow((int) (value * 1000));
         }
 
         refresh();
     }
 
-    public static void removeTracks() {
+    public static void changeFontSize(final Collection<Track> selectedTracks) {
+
 
-        HashSet<Track> selectedTracks = new HashSet<Track>(getSelectedTracks());
         if (selectedTracks.isEmpty()) {
             return;
         }
 
-        StringBuffer buffer = new StringBuffer();
-        for (Track track : selectedTracks) {
-            buffer.append("\n\t");
-            buffer.append(track.getDisplayName());
-        }
-        String deleteItems = buffer.toString();
-
-        JTextArea textArea = new JTextArea();
-        textArea.setEditable(false);
-        JScrollPane scrollPane = new JScrollPane(textArea);
-        textArea.setText(deleteItems);
-
-        JOptionPane optionPane = new JOptionPane(scrollPane,
-                JOptionPane.PLAIN_MESSAGE,
-                JOptionPane.YES_NO_OPTION);
-        optionPane.setPreferredSize(new Dimension(550, 500));
-        JDialog dialog = optionPane.createDialog(IGVMainFrame.getInstance(),
-                "Remove The Following Tracks");
-        dialog.setVisible(true);
-
-        Object choice = optionPane.getValue();
-        if ((choice == null) || (JOptionPane.YES_OPTION != ((Integer) choice).intValue())) {
+        final String parameter = "Font size";
+        int defaultValue = selectedTracks.iterator().next().getFontSize();
+        int value = getIntValue(parameter, defaultValue);
+        if (value == Integer.MIN_VALUE) {
             return;
         }
 
-        TrackManager.getInstance().removeTracks(selectedTracks);
-
-        IGVMainFrame.getInstance().doResizeTrackPanels();
+        for (Track track : selectedTracks) {
+            track.setFontSize(value);
+        }
 
         refresh();
     }
 
-    public static void changeTrackColor() {
 
-        HashSet<Track> selectedTracks = new HashSet<Track>(getSelectedTracks());
+    private static int getIntValue(String parameter, int value) {
+
+        while (true) {
+
+            String height = JOptionPane.showInputDialog(
+                    IGVMainFrame.getInstance(), parameter + ": ",
+                    String.valueOf(value));
+
+            if ((height == null) || height.trim().equals("")) {
+                return Integer.MIN_VALUE;   // <= the logical "null" value
+            }
+
+            try {
+                value = Integer.parseInt(height);
+                return value;
+            } catch (NumberFormatException numberFormatException) {
+                JOptionPane.showMessageDialog(IGVMainFrame.getInstance(),
+                        parameter + " must be an integer number.");
+            }
+        }
+    }
+
+    public static void changeTrackColor(final Collection<Track> selectedTracks) {
 
         if (selectedTracks.isEmpty()) {
             return;
@@ -650,15 +803,13 @@ public class TrackMenuUtils {
             for (Track track : selectedTracks) {
                 track.setColor(color);
             }
-
+            clearTrackSelections();
             refresh();
         }
 
     }
 
-    public static void changeAltTrackColor() {
-
-        HashSet<Track> selectedTracks = new HashSet<Track>(getSelectedTracks());
+    public static void changeAltTrackColor(final Collection<Track> selectedTracks) {
 
         if (selectedTracks.isEmpty()) {
             return;
@@ -678,13 +829,53 @@ public class TrackMenuUtils {
 
             track.setAltColor(color);
         }
-
+        clearTrackSelections();
         refresh();
 
     }
 
-    static public Collection<Track> getSelectedTracks() {
-        return TrackManager.getInstance().getSelectedTracks();
+    public static JMenuItem getCopyDetailsItem(final Feature f, final int mouseX) {
+        JMenuItem item = new JMenuItem("Copy Details to Clipboard");
+        item.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                double location = ViewContext.getInstance().getChromosomePosition(mouseX);
+                String details = f.getValueString(location, null);
+                if (details != null) {
+                    details = details.replace("<br>", System.getProperty("line.separator"));
+                    details += System.getProperty("line.separator") +
+                            f.getChr() + ":" + (f.getStart() + 1) + "-" + f.getEnd();
+                    StringSelection stringSelection = new StringSelection(details);
+                    Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+                    clipboard.setContents(stringSelection, null);
+                }
+            }
+        });
+        return item;
+    }
+
+
+    public static JMenuItem getCopySequenceItem(final Feature f) {
+        JMenuItem item = new JMenuItem("Copy Sequence to Clipboard");
+        item.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                String genomeId = ViewContext.getInstance().getGenomeId();
+                String chr = f.getChr();
+                int start = f.getStart();
+                int end = f.getEnd();
+                byte[] seqBytes = SequenceManager.readSequence(genomeId, chr, start, end);
+                if (seqBytes == null) {
+                    MessageUtils.showMessage("Sequence not available");
+                } else {
+                    String sequence = new String(seqBytes);
+                    StringSelection stringSelection = new StringSelection(sequence);
+                    Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+                    clipboard.setContents(stringSelection, null);
+                }
+            }
+        });
+        return item;
     }
 
     /**
@@ -701,8 +892,7 @@ public class TrackMenuUtils {
             heights[i] = track.getHeight();
             i++;
         }
-        int medianTrackHeight = (int) Math.round(StatUtils.percentile(heights,
-                50));
+        int medianTrackHeight = (int) Math.round(StatUtils.percentile(heights, 50));
         if (medianTrackHeight > 0) {
             return medianTrackHeight;
         }
@@ -712,33 +902,59 @@ public class TrackMenuUtils {
     }
 
     public static void refresh() {
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
 
                 IGVMainFrame.getInstance().showLoadedTrackCount();
-                IGVMainFrame.getInstance().clearImageCacheWithNoRepaint();
                 IGVMainFrame.getInstance().getContentPane().repaint();
             }
         });
     }
 
-    public static JMenuItem addChangeTrackHeightItem(JPopupMenu menu) {
+    public static JMenuItem getChangeTrackHeightItem(final Collection<Track> selectedTracks) {
         // Change track height by attribute
-        JMenuItem item = new JMenuItem("Change Track Height");
+        JMenuItem item = new JMenuItem("Change Track Height...");
         item.addActionListener(new ActionListener() {
 
             public void actionPerformed(ActionEvent e) {
-                GuiUtilities.invokeOnEventThread(new Runnable() {
+                changeTrackHeight(selectedTracks);
+                clearTrackSelections();
+            }
+        });
+        return item;
+    }
 
-                    public void run() {
-                        changeTrackHeight();
-                    }
-                });
+    public static JMenuItem getChangeFeatureWindow(final Collection<Track> selectedTracks) {
+        // Change track height by attribute
+        JMenuItem item = new JMenuItem("Set Feature Visibility Window...");
+        item.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                changeFeatureVisibilityWindow(selectedTracks);
+                clearTrackSelections();
             }
         });
-        menu.add(item);
         return item;
     }
+
+    public static JMenuItem getChangeFontSizeItem(final Collection<Track> selectedTracks) {
+        // Change track height by attribute
+        JMenuItem item = new JMenuItem("Change Font Size");
+        item.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                changeFontSize(selectedTracks);
+                clearTrackSelections();
+            }
+        });
+        return item;
+    }
+
+
+    public static void clearTrackSelections() {
+        IGVMainFrame.getInstance().getTrackManager().clearSelections();
+    }
+
 }
 
diff --git a/src/org/broad/igv/track/TrackProperties.java b/src/org/broad/igv/track/TrackProperties.java
index 35fa0d2..aa5eb99 100644
--- a/src/org/broad/igv/track/TrackProperties.java
+++ b/src/org/broad/igv/track/TrackProperties.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -30,6 +30,13 @@ import java.awt.*;
  * @author jrobinso
  */
 public class TrackProperties {
+    public boolean isLogScale() {
+        return logScale;
+    }
+
+    public void setLogScale(boolean logScale) {
+        this.logScale = logScale;
+    }
 
     public enum BaseCoord {
         ZERO, ONE, UNSPECIFIED
@@ -102,6 +109,39 @@ public class TrackProperties {
 
     private int smoothingWindow;
 
+    private boolean itemRGB = true;
+
+    private boolean useScore = false;
+
+    private int featureVisibilityWindow = -1;
+
+    private boolean logScale;
+
+
+    public int getFeatureVisibilityWindow() {
+        return featureVisibilityWindow;
+    }
+
+    public void setFeatureVisibilityWindow(int featureVisibilityWindow) {
+        this.featureVisibilityWindow = featureVisibilityWindow;
+    }
+
+    public boolean isUseScore() {
+        return useScore;
+    }
+
+    public void setUseScore(boolean useScore) {
+        this.useScore = useScore;
+    }
+
+    public boolean isItemRGB() {
+        return itemRGB;
+    }
+
+    public void setItemRGB(boolean itemRGB) {
+        this.itemRGB = itemRGB;
+    }
+
     /**
      * Method description
      *
@@ -111,235 +151,128 @@ public class TrackProperties {
         return offset;
     }
 
-    /**
-     * Method description
-     *
-     * @param offset
-     */
     public void setOffset(int offset) {
         this.offset = offset;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public String getName() {
         return name;
     }
 
-    /**
-     * Method description
-     *
-     * @param name
-     */
     public void setName(String name) {
         this.name = name;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public String getDescription() {
         return description;
     }
 
-    /**
-     * Method description
-     *
-     * @param description
-     */
+
     public void setDescription(String description) {
         this.description = description;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public String getUrl() {
         return url;
     }
 
-    /**
-     * Method description
-     *
-     * @param url
-     */
+
     public void setUrl(String url) {
         this.url = url;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public int getHeight() {
         return height;
     }
 
-    /**
-     * Method description
-     *
-     * @param height
-     */
+
     public void setHeight(int height) {
         this.height = height;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public Color getColor() {
         return color;
     }
 
-    /**
-     * Method description
-     *
-     * @param color
-     */
+
     public void setColor(Color color) {
         this.color = color;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public Color getAltColor() {
         return altColor;
     }
 
-    /**
-     * Method description
-     *
-     * @param altColor
-     */
+
     public void setAltColor(Color altColor) {
         this.altColor = altColor;
     }
 
-    /**
-     * Autoscale if either (1) autoscale is explictly set on,  or (2) view limits are not provided
-     */
+
     public boolean isAutoScale() {
         return autoScaleFlag || Float.isNaN(minValue) || Float.isNaN(maxValue);
     }
 
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public String getGenome() {
         return genome;
     }
 
-    /**
-     * Method description
-     *
-     * @param genome
-     */
+
     public void setGenome(String genome) {
         this.genome = genome;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public float getMinValue() {
         return minValue;
     }
 
-    /**
-     * Method description
-     *
-     * @param minValue
-     */
+
     public void setMinValue(float minValue) {
         this.minValue = minValue;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public float getMaxValue() {
         return maxValue;
     }
 
-    /**
-     * Method description
-     *
-     * @param maxValue
-     */
+
     public void setMaxValue(float maxValue) {
         this.maxValue = maxValue;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public WindowFunction getWindowingFunction() {
         return windowingFunction;
     }
 
-    /**
-     * Method description
-     *
-     * @param windowingFunction
-     */
+
     public void setWindowingFunction(WindowFunction windowingFunction) {
         this.windowingFunction = windowingFunction;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public int getSmoothingWindow() {
         return smoothingWindow;
     }
 
-    /**
-     * Method description
-     *
-     * @param smoothingWindow
-     */
+
     public void setSmoothingWindow(int smoothingWindow) {
         this.smoothingWindow = smoothingWindow;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public Class getRendererClass() {
         return rendererClass;
     }
 
-    /**
-     * Method description
-     *
-     * @param rendererClass
-     */
     public void setRendererClass(Class rendererClass) {
         this.rendererClass = rendererClass;
     }
diff --git a/src/org/broad/igv/track/TrackReader.java b/src/org/broad/igv/track/TrackReader.java
index cac1965..fc5ab79 100644
--- a/src/org/broad/igv/track/TrackReader.java
+++ b/src/org/broad/igv/track/TrackReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/track/TrackSet.java b/src/org/broad/igv/track/TrackSet.java
index 291dc96..c38e542 100644
--- a/src/org/broad/igv/track/TrackSet.java
+++ b/src/org/broad/igv/track/TrackSet.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/track/TrackType.java b/src/org/broad/igv/track/TrackType.java
index 7497a42..eb5bb06 100644
--- a/src/org/broad/igv/track/TrackType.java
+++ b/src/org/broad/igv/track/TrackType.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -27,6 +27,7 @@ package org.broad.igv.track;
  * @author jrobinso
  */
 public enum TrackType {
-    GENERIC, OTHER, COPY_NUMBER, GENE_EXPRESSION, CHIP, DNA_METHYLATION, TILING_ARRAY, PHASTCON,
-    ALLELE_SPECIFIC_COPY_NUMBER, LOH, MUTATION, RNAI, POOLED_RNAI, CHIP_CHIP, CNV, UNKNOWN
+    OTHER, COPY_NUMBER, GENE_EXPRESSION, CHIP, DNA_METHYLATION, TILING_ARRAY, PHASTCON,
+    ALLELE_SPECIFIC_COPY_NUMBER, LOH, MUTATION, RNAI, POOLED_RNAI, CHIP_CHIP, CNV,
+    ALLELE_FREQUENCY, COVERAGE, REPMASK
 }
diff --git a/src/org/broad/igv/track/WindowFunction.java b/src/org/broad/igv/track/WindowFunction.java
index 1c28541..5d3d1eb 100644
--- a/src/org/broad/igv/track/WindowFunction.java
+++ b/src/org/broad/igv/track/WindowFunction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/track/tribble/BEDCodec.java b/src/org/broad/igv/track/tribble/BEDCodec.java
new file mode 100644
index 0000000..1c74c63
--- /dev/null
+++ b/src/org/broad/igv/track/tribble/BEDCodec.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track.tribble;
+
+import org.broad.igv.feature.BasicFeature;
+import org.broad.igv.feature.Exon;
+import org.broad.igv.feature.Strand;
+import org.broad.igv.track.TrackProperties;
+import org.broad.igv.track.TrackType;
+import org.broad.igv.util.ParsingUtils;
+import org.broad.tribble.Feature;
+import org.broad.tribble.exception.CodecLineParsingException;
+import org.broad.tribble.util.LineReader;
+
+import java.awt.*;
+import java.io.IOException;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: Dec 20, 2009
+ * Time: 10:15:49 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class BEDCodec extends UCSCCodec {
+
+    // Declare a static array once, to be reused.
+
+    public BasicFeature decode(String nextLine) {
+
+        if (nextLine.trim().length() == 0 || nextLine.startsWith("#") || nextLine.startsWith("track") ||
+                nextLine.startsWith("browser")) {
+            return null;
+        }
+
+        int tokenCount = ParsingUtils.splitWhitespace(nextLine, tokens);
+
+        // The first 3 columns are non optional for BED.  We will relax this
+        // and only require 2.
+
+        if (tokenCount < 2) {
+            return null;
+        }
+
+        String chr = tokens[0];  //genome == null ? tokens[0] : genome.getChromosomeAlias(tokens[0]);
+        int start = Integer.parseInt(tokens[1]) - startBase;
+
+        int end = start + 1;
+        if (tokenCount > 2) {
+            end = Integer.parseInt(tokens[2]) - startBase;
+        }
+
+        BasicFeature feature = new BasicFeature(chr, start, end);
+
+        // The rest of the columns are optional.  Stop parsing upon encountering
+        // a non-expected value
+
+        // Name
+        if (tokenCount > 3) {
+            String name = tokens[3].replaceAll("\"", "");
+            feature.setName(name);
+            feature.setIdentifier(name);
+        }
+
+        // Score
+        if (tokenCount > 4) {
+            try {
+                float score = Float.parseFloat(tokens[4]);
+                feature.setScore(score);
+            } catch (NumberFormatException numberFormatException) {
+
+                // Unexpected, but does not invalidate the previous values.
+                // Stop parsing the line here but keep the feature
+                // Don't log, would just slow parsing down.
+                return feature;
+            }
+        }
+
+        // Strand
+        if (tokenCount > 5) {
+            String strandString = tokens[5].trim();
+            char strand = (strandString.length() == 0)
+                    ? ' ' : strandString.charAt(0);
+
+            if (strand == '-') {
+                feature.setStrand(Strand.NEGATIVE);
+            } else if (strand == '+') {
+                feature.setStrand(Strand.POSITIVE);
+            } else {
+                feature.setStrand(Strand.NONE);
+            }
+        }
+
+        if (tokenCount > 8) {
+            String colorString = tokens[8];
+            feature.setColor(parseColor(colorString));
+        }
+
+        // Coding information is optional
+        if (tokenCount > 11) {
+            createExons(start, tokens, feature, chr, feature.getStrand());
+        }
+
+        return feature;
+    }
+
+
+    private void createExons(int start, String[] tokens, BasicFeature gene, String chr,
+                             Strand strand) throws NumberFormatException {
+
+        int cdStart = Integer.parseInt(tokens[6]);
+        int cdEnd = Integer.parseInt(tokens[7]);
+
+        int exonCount = Integer.parseInt(tokens[9]);
+        String[] exonSizes = new String[exonCount];
+        String[] startsBuffer = new String[exonCount];
+        ParsingUtils.split(tokens[10], exonSizes, ',');
+        ParsingUtils.split(tokens[11], startsBuffer, ',');
+
+        int exonNumber = (strand == Strand.NEGATIVE ? exonCount : 1);
+
+        if (startsBuffer.length == exonSizes.length) {
+            for (int i = 0; i < startsBuffer.length; i++) {
+                int exonStart = start + Integer.parseInt(startsBuffer[i]);
+                int exonEnd = exonStart + Integer.parseInt(exonSizes[i]);
+                Exon exon = new Exon(chr, exonStart, exonEnd, strand);
+                exon.setCodingStart(cdStart);
+                exon.setCodingEnd(cdEnd);
+                exon.setNumber(exonNumber);
+                gene.addExon(exon);
+
+                if (strand == Strand.NEGATIVE) {
+                    exonNumber--;
+                } else {
+                    exonNumber++;
+                }
+            }
+        }
+    }
+
+
+    /**
+     * TODO -- move this to a utility class
+     *
+     * @param colorString
+     * @return
+     */
+    private Color parseColor(String colorString) {
+        String[] rgb = new String[3];
+        int nTokens = ParsingUtils.split(tokens[8].replaceAll("\"", ""), rgb, ',');
+
+        try {
+            if (nTokens < 3) {
+                // TODO -- is this the right constructor to use?
+                return new Color(Integer.parseInt(rgb[0]));
+            } else {
+                return new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]),
+                        Integer.parseInt(rgb[2]));
+            }
+        } catch (NumberFormatException numberFormatException) {
+            //log.error("Error parsing color from rgb string: " + rgb[0] + rgb[1] + rgb[2]);
+            return null;
+        }
+    }
+
+
+}
\ No newline at end of file
diff --git a/src/org/broad/igv/track/tribble/CachingFeatureReader.java b/src/org/broad/igv/track/tribble/CachingFeatureReader.java
new file mode 100644
index 0000000..9e693b0
--- /dev/null
+++ b/src/org/broad/igv/track/tribble/CachingFeatureReader.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track.tribble;
+
+import net.sf.samtools.util.CloseableIterator;
+import org.apache.log4j.Logger;
+import org.broad.igv.exceptions.DataLoadException;
+import org.broad.igv.feature.Genome;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.ui.util.MessageUtils;
+import org.broad.igv.util.LRUCache;
+import org.broad.igv.util.RuntimeUtils;
+import org.broad.tribble.Feature;
+import org.broad.tribble.FeatureReader;
+import org.broad.tribble.util.CloseableTribbleIterator;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.io.IOException;
+import java.util.Set;
+
+
+/**
+ * Author: jrobinso
+ * Date: Jun 24, 2010
+ */
+public class CachingFeatureReader implements FeatureReader {
+
+    private static Logger log = Logger.getLogger(CachingFeatureReader.class);
+    String cachedChr = "";
+    static int maxTileCount = 5;
+    private static int defaultTileSize = 1000000;
+    private int tileSize = defaultTileSize;
+    FeatureReader reader;
+    LRUCache<Integer, Tile> cache;
+
+
+    public CachingFeatureReader(FeatureReader reader) {
+        this(reader, defaultTileSize);
+    }
+
+
+    public CachingFeatureReader(FeatureReader reader, int tileSize) {
+        this.reader = reader;
+        this.cache = new LRUCache(this, maxTileCount);
+        this.tileSize = tileSize;
+    }
+
+    public void setTileSize(int tileSize) {
+        cache.clear();
+        this.tileSize = tileSize;
+
+    }
+
+    public Set<String> getSequenceNames() {
+        return reader.getSequenceNames();
+    }
+
+    /**
+     * Return an iterator over the entire file.  Nothing to cache,  just delegate to the wrapped reader
+     *
+     * @throws IOException
+     */
+    public CloseableTribbleIterator iterator() throws IOException {
+        return reader.iterator();
+    }
+
+    public void close() throws IOException {
+        cache.clear();
+        reader.close();
+    }
+
+    public CloseableTribbleIterator query(String chr, int start, int end) throws IOException {
+        return query(chr, start, end, false);
+    }
+
+    /**
+     * TODO -- implement contained support (specifically contained == true).  Not needed for IGV
+     * <p/>
+     * tileSize == 0 implies cache by whole chromosome (1 tile)
+     *
+     * @param chr
+     * @param start
+     * @param end
+     * @param contained
+     * @return
+     * @throws IOException
+     */
+    public CloseableTribbleIterator query(String chr, int start, int end, boolean contained) throws IOException {
+
+        int startTile = 0;
+        int endTile = 0;
+        if (tileSize > 0) {
+            startTile = start / tileSize;
+            endTile = end / tileSize;    // <= inclusive
+        }
+        List<Tile> tiles = getTiles(chr, startTile, endTile);
+
+        if (tiles.size() == 0) {
+            return null;
+        }
+
+        // Count total # of records
+        int recordCount = tiles.get(0).getOverlappingRecords().size();
+        for (Tile t : tiles) {
+            recordCount += t.getContainedRecords().size();
+        }
+
+        List<Feature> alignments = new ArrayList(recordCount);
+        alignments.addAll(tiles.get(0).getOverlappingRecords());
+        for (Tile t : tiles) {
+            alignments.addAll(t.getContainedRecords());
+        }
+        return new TiledIterator(start, end, alignments);
+    }
+
+
+    /**
+     * Return loaded tiles that span the query interval.
+     *
+     * @param seq
+     * @param startTile
+     * @param endTile
+     * @return
+     */
+    private List<Tile> getTiles(String seq, int startTile, int endTile) {
+
+        if (!seq.equals(cachedChr)) {
+            cache.clear();
+            cachedChr = seq;
+        }
+
+
+        List<Tile> tiles = new ArrayList(endTile - startTile + 1);
+        List<Tile> tilesToLoad = new ArrayList(endTile - startTile + 1);
+
+        for (int t = startTile; t <= endTile; t++) {
+            Tile tile = cache.get(t);
+
+            if (tile == null) {
+                int start = t * tileSize;
+                int end = start + tileSize;
+                tile = new Tile(t, start, end);
+                cache.put(t, tile);
+            }
+
+            tiles.add(tile);
+
+            // The current tile is loaded,  load any preceding tiles we have pending
+            if (tile.isLoaded()) {
+                if (tilesToLoad.size() > 0) {
+                    if (!loadTiles(seq, tilesToLoad)) {
+                        return tiles;
+                    }
+                }
+                tilesToLoad.clear();
+            } else {
+                tilesToLoad.add(tile);
+            }
+        }
+
+        if (tilesToLoad.size() > 0) {
+            loadTiles(seq, tilesToLoad);
+        }
+
+        return tiles;
+    }
+
+    private boolean loadTiles(String seq, List<Tile> tiles) {
+
+        assert (tiles.size() > 0);
+
+        if (log.isDebugEnabled()) {
+            int first = tiles.get(0).getTileNumber();
+            int end = tiles.get(tiles.size() - 1).getTileNumber();
+            log.debug("Loading tiles: " + first + "-" + end);
+        }
+
+        int start = tiles.get(0).start;
+        int end = tiles.get(tiles.size() - 1).end;
+        CloseableIterator<Feature> iter = null;
+
+        try {
+            iter = reader.query(seq, start, end);
+
+            while (iter != null && iter.hasNext()) {
+                Feature record = iter.next();
+
+                // Range of tile indeces that this alignment contributes to.
+                int aStart = record.getStart();
+                int aEnd = record.getEnd();
+                int idx0 = 0;
+                int idx1 = 0;
+                if (tileSize > 0) {
+                    idx0 = Math.max(0, (aStart - start) / tileSize);
+                    idx1 = Math.min(tiles.size() - 1, (aEnd - start) / tileSize);
+                }
+
+                // Loop over tiles this read overlaps
+                for (int i = idx0; i <= idx1; i++) {
+                    Tile t = tiles.get(i);
+
+                    // A tile end of zero => the tile represents the entire chromosome
+                    if (t.end <= 0) {
+                        t.containedRecords.add(record);
+                    } else {
+                        if ((aStart >= t.start) && (aStart < t.end)) {
+                            t.containedRecords.add(record);
+                        } else if ((aEnd >= t.start) && (aStart < t.start)) {
+                            t.overlappingRecords.add(record);
+                        }
+                    }
+                }
+            }
+
+            for (Tile t : tiles) {
+                t.setLoaded(true);
+            }
+
+            return true;
+
+        } catch (Exception e) {
+            log.error("Error loading alignment data", e);
+            throw new DataLoadException("", "Error: " + e.toString());
+        }
+
+        finally {
+            if (iter != null) {
+                iter.close();
+            }
+            //IGVMainFrame.getInstance().resetStatusMessage();
+        }
+    }
+
+    private boolean checkMemory() {
+        if (RuntimeUtils.getAvailableMemory() < 50000000) {
+            LRUCache.clearCaches();
+            if (RuntimeUtils.getAvailableMemory() < 50000000) {
+                String msg = "Memory is low, reading terminating.";
+                MessageUtils.showMessage(msg);
+                return false;
+            }
+        }
+        return true;
+    }
+
+
+    static class Tile {
+
+        private boolean loaded = false;
+        private int start;
+        private int end;
+        private int tileNumber;
+        private List<Feature> containedRecords;
+        private List<Feature> overlappingRecords;
+
+        Tile(int tileNumber, int start, int end) {
+            this.tileNumber = tileNumber;
+            this.start = start;
+            this.end = end;
+            containedRecords = new ArrayList(1000);
+            overlappingRecords = new ArrayList(100);
+        }
+
+        public int getTileNumber() {
+            return tileNumber;
+        }
+
+
+        public int getStart() {
+            return start;
+        }
+
+        public void setStart(int start) {
+            this.start = start;
+        }
+
+        public List<Feature> getContainedRecords() {
+            return containedRecords;
+        }
+
+        public List<Feature> getOverlappingRecords() {
+            return overlappingRecords;
+        }
+
+        public boolean isLoaded() {
+            return loaded;
+        }
+
+        public void setLoaded(boolean loaded) {
+            this.loaded = loaded;
+        }
+
+    }
+
+    /**
+     * TODO -- this is a pointeless class.  It would make sense if it actually took tiles, instead of the collection
+     * TODO -- of alignments.
+     */
+    public class TiledIterator implements CloseableTribbleIterator {
+
+        Iterator<Feature> currentSamIterator;
+        int end;
+        Feature nextRecord;
+        int start;
+        List<Feature> alignments;
+
+        TiledIterator(int start, int end, List<Feature> alignments) {
+            this.alignments = alignments;
+            this.start = start;
+            this.end = end;
+            currentSamIterator = alignments.iterator();
+            advanceToFirstRecord();
+        }
+
+        public void close() {
+            // No-op
+        }
+
+        public boolean hasNext() {
+            return nextRecord != null;
+        }
+
+        public Feature next() {
+            Feature ret = nextRecord;
+
+            advanceToNextRecord();
+
+            return ret;
+        }
+
+        public void remove() {
+            // ignored
+        }
+
+        private void advanceToFirstRecord() {
+            advanceToNextRecord();
+        }
+
+        private void advanceToNextRecord() {
+            advance();
+
+            while ((nextRecord != null) && (nextRecord.getEnd() < start)) {
+                advance();
+            }
+        }
+
+        private void advance() {
+            if (currentSamIterator.hasNext()) {
+                nextRecord = currentSamIterator.next();
+                if (nextRecord.getStart() > end) {
+                    nextRecord = null;
+                }
+            } else {
+                nextRecord = null;
+            }
+        }
+
+        public Iterator iterator() {
+            return this;
+        }
+    }
+}
+
diff --git a/src/org/broad/igv/track/tribble/CodecFactory.java b/src/org/broad/igv/track/tribble/CodecFactory.java
new file mode 100644
index 0000000..ea06996
--- /dev/null
+++ b/src/org/broad/igv/track/tribble/CodecFactory.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track.tribble;
+
+import org.broad.igv.exceptions.DataLoadException;
+import org.broad.tribble.FeatureCodec;
+
+/**
+ * 
+ */
+public class CodecFactory {
+    public static FeatureCodec getCodec(String file) {
+
+        String fn = file.toLowerCase();
+        if (fn.endsWith(".gz")) {
+            int l = fn.length() - 3;
+            fn = fn.substring(0, l);
+        }
+        if (fn.endsWith(".vcf") || (fn.endsWith(".vcf4"))) {
+            return new VCFCodec();
+        } else if (fn.endsWith(".bed")) {
+            return new BEDCodec();
+        } else if (fn.endsWith(".psl") ||fn.endsWith(".pslx")) {
+            return new PSLCodec();
+        } else if (fn.endsWith(".repmask")) {
+            return new REPMaskCodec();
+        } else if (fn.endsWith(".gff3")) {
+            return new GFFCodec(GFFCodec.Version.GFF3);
+        } else if (fn.endsWith(".gff")) {
+            return new GFFCodec();
+        } else if (fn.endsWith(".sam")) {
+            return new SAMCodec();
+        } else {
+            throw new DataLoadException("Unknown file type", file);
+        }
+
+    }
+}
diff --git a/src/org/broad/igv/track/tribble/FeatureFileHeader.java b/src/org/broad/igv/track/tribble/FeatureFileHeader.java
new file mode 100644
index 0000000..772d23e
--- /dev/null
+++ b/src/org/broad/igv/track/tribble/FeatureFileHeader.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track.tribble;
+
+import org.broad.igv.track.TrackProperties;
+import org.broad.igv.track.TrackType;
+
+import java.util.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: May 22, 2010
+ * Time: 7:10:59 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class FeatureFileHeader {
+
+    /* An object to collection track properties, if specified in the feature file. */
+    private TrackProperties trackProperties;
+
+    private TrackType trackType;
+
+    private Set<String> featuresToHide = new HashSet();
+
+    public TrackProperties getTrackProperties() {
+        return trackProperties;
+    }
+
+    public TrackType getTrackType() {
+        return trackType;
+    }
+
+    public void setTrackProperties(TrackProperties trackProperties) {
+        this.trackProperties = trackProperties;
+    }
+
+    public void setTrackType(TrackType trackType) {
+        this.trackType = trackType;
+    }
+
+    public Set<String> getFeaturesToHide() {
+        return featuresToHide;
+    }
+
+    public void setFeaturesToHide(Collection<String> featuresToHide) {
+        this.featuresToHide.addAll(featuresToHide);
+    }
+}
diff --git a/src/org/broad/igv/track/tribble/GFFCodec.java b/src/org/broad/igv/track/tribble/GFFCodec.java
new file mode 100644
index 0000000..5b892e6
--- /dev/null
+++ b/src/org/broad/igv/track/tribble/GFFCodec.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track.tribble;
+
+import org.apache.log4j.Logger;
+import org.broad.igv.exceptions.DataLoadException;
+import org.broad.igv.feature.BasicFeature;
+import org.broad.igv.feature.Strand;
+import org.broad.igv.track.TrackProperties;
+import org.broad.igv.util.ColorUtilities;
+import org.broad.igv.util.ParsingUtils;
+import org.broad.igv.util.StringUtils;
+import org.broad.tribble.*;
+import org.broad.tribble.exception.CodecLineParsingException;
+import org.broad.tribble.util.LineReader;
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Notes from GFF3 spec
+ * These tags have predefined meanings:
+ * <p/>
+ * ID	   Indicates the name of the feature.  IDs must be unique
+ * within the scope of the GFF file.
+ * <p/>
+ * Name   Display name for the feature.  This is the name to be
+ * displayed to the user.  Unlike IDs, there is no requirement
+ * that the Name be unique within the file.
+ * <p/>
+ * Alias  A secondary name for the feature.  It is suggested that
+ * this tag be used whenever a secondary identifier for the
+ * feature is needed, such as locus names and
+ * accession numbers.  Unlike ID, there is no requirement
+ * that Alias be unique within the file.
+ * <p/>
+ * Parent Indicates the parent of the feature.  A parent ID can be
+ * used to group exons into transcripts, transcripts into
+ * genes, an so forth.  A feature may have multiple parents.
+ * Parent can *only* be used to indicate a partof
+ * relationship.
+ */
+public class GFFCodec implements org.broad.tribble.FeatureCodec {
+
+    private static Logger log = Logger.getLogger(GFFCodec.class);
+
+    public enum Version {
+        GFF2, GFF3
+    }
+
+    static String[] nameFields = {"Name", "name", "gene", "primary_name", "Locus", "locus",
+            "alias", "systematic_id", "ID"};
+
+
+    FeatureFileHeader header;
+
+    Helper helper;
+
+    String[] tokens = new String[10];
+
+    public GFFCodec() {
+        // Assume GFF2 until shown otherwise
+        helper = new GFF2Helper();
+    }
+
+    public GFFCodec(Version version) {
+        if (version == Version.GFF2) {
+            helper = new GFF2Helper();
+        } else {
+            helper = new GFF3Helper();
+        }
+    }
+
+
+    public Object readHeader(LineReader reader) {
+
+
+        header = new FeatureFileHeader();
+        String line;
+        TrackProperties trackProperties;
+        int nLines = 0;
+
+        try {
+            while ((line = reader.readLine()) != null) {
+
+                if (line.startsWith("#")) {
+                    nLines++;
+                    if (line.startsWith("##gff-version") && line.endsWith("3")) {
+                        helper = new GFF3Helper();
+                    } else if (line.startsWith("#track") || line.startsWith("##track")) {
+                        trackProperties = new TrackProperties();
+                        ParsingUtils.parseTrackLine(line, trackProperties);
+                    } else if (line.startsWith("#nodecode") || line.startsWith("##nodecode")) {
+                        helper.setUrlDecoding(false);
+                    } else if (line.startsWith("#hide") || line.startsWith("##hide")) {
+                        String[] kv = line.split("=");
+                        if (kv.length > 1) {
+                            header.setFeaturesToHide(Arrays.asList(kv[1].split(",")));
+                        }
+                    }
+                    continue;
+                } else {
+                    break;
+                }
+            }
+
+            return header;
+        }
+        catch (IOException e) {
+            throw new CodecLineParsingException("Error parsing header", e);
+        }
+    }
+
+    public BasicFeature decodeLoc(String line) {
+        return decode(line);
+    }
+
+    public BasicFeature decode(String line) {
+
+
+        if (line.startsWith("##gff-version") && line.endsWith("3")) {
+            helper = new GFF3Helper();
+        }
+
+
+        if (line.startsWith("#")) {
+            return null;
+        }
+
+
+        int nTokens = ParsingUtils.split(line, tokens, '\t');
+
+        // GFF files have 9 tokens
+        if (nTokens < 9) {
+            return null;
+        }
+
+        // The type
+        String featureType = new String(tokens[2].trim());
+
+
+        String chromosome = tokens[0];  //genome.getChromosomeAlias(tokens[0]);
+
+        // GFF coordinates are 1-based inclusive (length = end - start + 1)
+        // IGV (UCSC) coordinates are 0-based exclusive.  Adjust start and end accordingly
+        int start;
+        int end;
+        try {
+            start = Integer.parseInt(tokens[3]) - 1;
+        }
+        catch (NumberFormatException ne) {
+            throw new DataLoadException("Column 4 must contain a numeric value", line);
+        }
+
+        try {
+            end = Integer.parseInt(tokens[4]);
+        }
+        catch (NumberFormatException ne) {
+            throw new DataLoadException("Column 5 must contain a numeric value", line);
+        }
+
+        Strand strand = convertStrand(tokens[6]);
+
+        String attributeString = tokens[8];
+
+        LinkedHashMap<String, String> attributes = new LinkedHashMap();
+        //attributes.put("Type", featureType);
+        helper.parseAttributes(attributeString, attributes);
+
+        String description = getDescription(attributes, featureType);
+
+        String id = helper.getID(attributes);
+
+        String[] parentIds = helper.getParentIds(attributes, attributeString);
+
+        BasicFeature f = new BasicFeature(chromosome, start, end, strand);
+        f.setName(getName(attributes));
+        f.setType(featureType);
+        f.setDescription(description);
+        f.setIdentifier(id);
+        f.setParentIds(parentIds);
+
+        if (attributes.containsKey("color")) {
+            f.setColor(ColorUtilities.getColorFromString(attributes.get("color")));
+        }
+        if (attributes.containsKey("Color")) {
+            f.setColor(ColorUtilities.getColorFromString(attributes.get("Color")));
+        }
+        return f;
+
+    }
+
+    public Class getFeatureType() {
+        return Feature.class;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public Object getHeader()  {
+        return header;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+
+    private Strand convertStrand(String strandString) {
+        Strand strand = Strand.NONE;
+        if (strandString.equals("-")) {
+            strand = Strand.NEGATIVE;
+        } else if (strandString.equals("+")) {
+            strand = Strand.POSITIVE;
+        }
+
+        return strand;
+    }
+
+
+    String getName(Map<String, String> attributes) {
+
+        if(attributes == null || attributes.size() == 0) {
+            return null;
+        }
+        for (String nf : nameFields) {
+            if (attributes.containsKey(nf)) {
+                return attributes.get(nf);
+            }
+        }
+
+        // If still nothing return the first attribute value
+        return attributes.values().iterator().next();
+    }
+
+    protected interface Helper {
+
+        String[] getParentIds(Map<String, String> attributes, String attributeString);
+
+        void parseAttributes(String attributeString, Map<String, String> map);
+
+        String getID(Map<String, String> attributes);
+
+        void setUrlDecoding(boolean b);
+
+    }
+
+
+    static StringBuffer buf = new StringBuffer();
+
+    static String getDescription(Map<String, String> attributes, String type) {
+        buf.setLength(0);
+        buf.append(type);
+        buf.append("<br>");
+        for (Map.Entry<String, String> att : attributes.entrySet()) {
+            String attValue = att.getValue().replaceAll(";", "<br>");
+            buf.append(att.getKey());
+            buf.append(" = ");
+            buf.append(attValue);
+            buf.append("<br>");
+        }
+
+        String description = buf.toString();
+
+        return description;
+    }
+
+    class GFF2Helper implements Helper {
+
+        String[] idFields = {"systematic_id", "ID", "Name", "name", "primary_name", "gene", "Locus", "locus", "alias"};
+
+
+        public void setUrlDecoding(boolean b) {
+            // Ignored,  GFF files are never url DECODED
+        }
+
+
+        public void parseAttributes(String description, Map<String, String> kvalues) {
+
+            List<String> kvPairs = StringUtils.breakQuotedString(description.trim(), ';');
+
+            for (String kv : kvPairs) {
+                List<String> tokens = StringUtils.breakQuotedString(kv, ' ');
+                if (tokens.size() >= 2) {
+                    String key = tokens.get(0).trim().replaceAll("\"", "");
+                    String value = tokens.get(1).trim().replaceAll("\"", "");
+                    kvalues.put(key, value);
+                }
+            }
+        }
+
+
+        public String[] getParentIds(Map<String, String> attributes, String attributeString) {
+
+            String[] parentIds = new String[1];
+            if (attributes.isEmpty()) {
+                parentIds[0] = attributeString;
+            } else {
+                parentIds[0] = attributes.get("id");
+                if (parentIds[0] == null) {
+                    parentIds[0] = attributes.get("mRNA");
+                }
+                if (parentIds[0] == null) {
+                    parentIds[0] = attributes.get("systematic_id");
+                }
+                if (parentIds[0] == null) {
+                    parentIds[0] = attributes.get("transcript_id");
+                }
+                if (parentIds[0] == null) {
+                    parentIds[0] = attributes.get("gene");
+                }
+                if (parentIds[0] == null) {
+                    parentIds[0] = attributes.get("transcriptId");
+                }
+                if (parentIds[0] == null) {
+                    parentIds[0] = attributes.get("proteinId");
+                }
+            }
+            return parentIds;
+
+        }
+
+
+        public String getID(Map<String, String> attributes) {
+            for (String nf : idFields) {
+                if (attributes.containsKey(nf)) {
+                    return attributes.get(nf);
+                }
+            }
+            return getName(attributes);
+        }
+    }
+
+    class GFF3Helper implements Helper {
+
+
+        private boolean useUrlDecoding = true;
+
+
+        public String[] getParentIds(Map<String, String> attributes, String ignored) {
+            String parentIdString = attributes.get("Parent");
+            if (parentIdString != null) {
+                return attributes.get("Parent").split(",");
+            } else {
+                return null;
+            }
+        }
+
+
+        public void parseAttributes(String description, Map<String, String> kvalues) {
+
+            List<String> kvPairs = StringUtils.breakQuotedString(description.trim(), ';');
+            for (String kv : kvPairs) {
+                //int nValues = ParsingUtils.split(kv, tmp, '=');
+                List<String> tmp = StringUtils.breakQuotedString(kv, '=');
+                int nValues = tmp.size();
+                if (nValues > 0) {
+                    String key = tmp.get(0).trim();
+                    String value = ((nValues == 1) ? "" : tmp.get(1).trim());
+
+                    if (useUrlDecoding) {
+                        key = URLDecoder.decode(key);
+                        value = URLDecoder.decode(value);
+                    }
+                    kvalues.put(key, value);
+                } else {
+                    log.info("No attributes: " + description);
+                }
+            }
+        }
+
+        public void setUrlDecoding(boolean useUrlDecoding) {
+            this.useUrlDecoding = useUrlDecoding;
+        }
+
+
+        public String getID(Map<String, String> attributes) {
+            String id = attributes.get("ID");
+            return id;
+        }
+    }
+}
diff --git a/src/org/broad/igv/track/tribble/Locus.java b/src/org/broad/igv/track/tribble/Locus.java
new file mode 100644
index 0000000..51453d2
--- /dev/null
+++ b/src/org/broad/igv/track/tribble/Locus.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track.tribble;
+
+import org.broad.tribble.Feature;
+
+/**
+ * A minimal representation of a SAM alignment.   Used for indexing only
+ *
+ * Author: jrobinso
+ *
+ */
+public class Locus implements Feature {
+
+    String chr;
+    int start;
+    int end;
+
+    public Locus(String chr, int end, int start) {
+        this.chr = chr;
+        this.end = end;
+        this.start = start;
+    }
+
+    public String getChr() {
+        return chr;
+    }
+
+    public int getStart() {
+        return start;  
+    }
+
+    public int getEnd() {
+        return end;
+    }
+}
diff --git a/src/org/broad/igv/track/tribble/PSLCodec.java b/src/org/broad/igv/track/tribble/PSLCodec.java
new file mode 100644
index 0000000..c6ae596
--- /dev/null
+++ b/src/org/broad/igv/track/tribble/PSLCodec.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track.tribble;
+
+import org.broad.igv.feature.BasicFeature;
+import org.broad.igv.feature.Exon;
+import org.broad.igv.feature.Strand;
+import org.broad.igv.util.ParsingUtils;
+import org.broad.tribble.Feature;
+
+/**
+ * @author jrobinso
+ * @date Aug 5, 2010
+ */
+public class PSLCodec extends UCSCCodec {
+
+    public Feature decode(String line) {
+
+        BasicFeature f = null;
+        try {
+            if (line.trim().length() == 0 || line.startsWith("#") || line.startsWith("track") ||
+                    line.startsWith("browser")) {
+                return null;
+            }
+
+            int nTokens = ParsingUtils.splitWhitespace(line, tokens);
+            if (nTokens < 21) {
+                // log.info("Skipping line ")
+                return null;
+            }
+            int tSize = Integer.parseInt(tokens[14]);
+            String chr = tokens[13];
+            int start = Integer.parseInt(tokens[15]); // IS PSL 1 or ZERO based,  closed or open?
+
+            String strandString = tokens[8];
+            Strand strand = strandString.startsWith("+") ? Strand.POSITIVE : Strand.NEGATIVE;
+
+            boolean gNeg = false;
+            if (strandString.length() > 1) {
+                gNeg = strandString.charAt(1) == '-';
+            }
+
+            f = new BasicFeature();
+            f.setName(tokens[9]);
+            f.setChr(chr);
+            f.setStart(start);
+            f.setEnd(Integer.parseInt(tokens[16]));
+            f.setStrand(strand);
+
+            int exonCount = Integer.parseInt(tokens[17]);
+            String[] exonSizes = tokens[18].split(",");
+            String[] startsBuffer = tokens[20].split(",");
+
+            if (startsBuffer.length == exonSizes.length && exonCount == startsBuffer.length) {
+                for (int i = 0; i < startsBuffer.length; i++) {
+                    int exonSize = Integer.parseInt(exonSizes[i]);
+                    int exonStart = Integer.parseInt(startsBuffer[i]);
+                    if (gNeg) {
+                        exonStart = tSize - exonStart - exonSize;
+                    }
+                    int exonEnd = exonStart + exonSize;
+                    Exon exon = new Exon(chr, exonStart, exonEnd, strand);
+                    f.addExon(exon);
+                }
+            } else {
+                // TODO -- warn
+            }
+        } catch (NumberFormatException e) {
+            return null;
+        }
+
+
+        return f;   
+    }
+}
diff --git a/src/org/broad/igv/track/tribble/REPMaskCodec.java b/src/org/broad/igv/track/tribble/REPMaskCodec.java
new file mode 100644
index 0000000..90b43dc
--- /dev/null
+++ b/src/org/broad/igv/track/tribble/REPMaskCodec.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track.tribble;
+
+import org.broad.igv.feature.BasicFeature;
+import org.broad.igv.feature.Strand;
+import org.broad.igv.track.TrackProperties;
+import org.broad.igv.track.TrackType;
+import org.broad.igv.util.ParsingUtils;
+import org.broad.tribble.Feature;
+import org.broad.tribble.FeatureCodec;
+import org.broad.tribble.exception.CodecLineParsingException;
+import org.broad.tribble.util.LineReader;
+
+import java.io.IOException;
+
+/**
+ * Basically BED format with some columns rearranged
+ */
+public class REPMaskCodec implements FeatureCodec {
+
+    // Declare a  array once, to be reused.
+    String[] tokens = new String[15];
+    int startBase = 0;
+    FeatureFileHeader header;
+
+    public Object readHeader(LineReader reader) {
+
+        String nextLine;
+        header = new FeatureFileHeader();
+        header.setTrackType(TrackType.REPMASK);
+        int nLines = 0;
+
+        try {
+            while ((nextLine = reader.readLine()) != null &&
+                    (nextLine.startsWith("#") || nextLine.startsWith("track")) ||
+                    nextLine.startsWith("browser")) {
+                nLines++;
+                if (nextLine.startsWith("#type")) {
+                    String[] tokens = nextLine.split("=");
+                    if (tokens.length > 1) {
+                        try {
+                            header.setTrackType(TrackType.valueOf(tokens[1]));
+                        } catch (Exception e) {
+                            // log.error("Error converting track type: " + tokens[1]);
+                        }
+                    }
+                } else if (nextLine.startsWith("track")) {
+                    TrackProperties tp = new TrackProperties();
+                    ParsingUtils.parseTrackLine(nextLine, tp);
+                    header.setTrackProperties(tp);
+                }
+            }
+            return header;
+        }
+        catch (IOException e) {
+            throw new CodecLineParsingException("Error parsing header", e);
+        }
+    }
+
+    public Feature decodeLoc(String line) {
+        return decode(line);
+    }
+
+    public BasicFeature decode(String nextLine) {
+
+        if (nextLine.trim().length() == 0 || nextLine.startsWith("#")) {
+            return null;
+        }
+
+        int tokenCount = ParsingUtils.splitWhitespace(nextLine, tokens);
+
+        // The first 3 columns are non optional for BED.  We will relax this
+        // and only require 2.
+
+        if (tokenCount < 2) {
+            return null;
+        }
+
+        String chr = tokens[0];  //genome == null ? tokens[0] : genome.getChromosomeAlias(tokens[0]);
+        int start = Integer.parseInt(tokens[1]) - startBase;
+
+        int end = start + 1;
+        if (tokenCount > 2) {
+            end = Integer.parseInt(tokens[2]);
+        }
+
+        BasicFeature feature = new BasicFeature(chr, start, end);
+
+        // The rest of the columns are optional.  Stop parsing upon encountering
+        // a non-expected value
+
+        // Strand
+        if (tokenCount > 3) {
+            String strandString = tokens[3].trim();
+            char strand = (strandString.length() == 0)  ? ' ' : strandString.charAt(0);
+
+            if (strand == '-') {
+                feature.setStrand(Strand.NEGATIVE);
+            } else if (strand == '+') {
+                feature.setStrand(Strand.POSITIVE);
+            } else {
+                feature.setStrand(Strand.NONE);
+            }
+        }
+
+        // Name
+        if (tokenCount > 4) {
+            String name = tokens[4].replaceAll("\"", "");
+            feature.setName(name);
+            feature.setIdentifier(name);
+        }
+
+
+        return feature;
+    }
+
+    public Class getFeatureType() {
+        return BasicFeature.class;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+}
\ No newline at end of file
diff --git a/src/org/broad/igv/track/tribble/SAMCodec.java b/src/org/broad/igv/track/tribble/SAMCodec.java
new file mode 100644
index 0000000..97acdd5
--- /dev/null
+++ b/src/org/broad/igv/track/tribble/SAMCodec.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track.tribble;
+
+import org.broad.igv.sam.reader.SamUtils;
+import org.broad.tribble.Feature;
+import org.broad.tribble.FeatureCodec;
+import org.broad.tribble.util.LineReader;
+import org.broad.tribble.util.ParsingUtils;
+
+/**
+ * Author: jrobinso
+ * Date: Jul 18, 2010
+ * Time: 3:48:18 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class SAMCodec implements FeatureCodec {
+
+    final static int FLAG_COL = 1;
+    final static int READ_UNMAPPED_FLAG = 0x4;
+
+    // From SAM specification
+
+    private static final int RNAME_COL = 2;
+    private static final int POS_COL = 3;
+    private static final int CIGAR_COL = 5;
+    private static final int NUM_REQUIRED_FIELDS = 11;
+
+    // Declare static for effeciency -- note THIS IS NOT THREAD SAFE
+    private static String [] fields = new String[NUM_REQUIRED_FIELDS];
+
+    public Feature decodeLoc(String line) {
+        return decode(line);  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public synchronized Feature decode(String line) {
+
+        if(line.startsWith("@")) {
+            return null;
+        }
+
+        final int numFields = ParsingUtils.split(line, fields, '\t');
+        if (numFields < NUM_REQUIRED_FIELDS ) {
+            return null;
+        }
+
+        String chr = fields[RNAME_COL];
+        int alignmentStart = Integer.parseInt(fields[POS_COL].trim());
+        String cigarString = fields[CIGAR_COL];
+        int len = SamUtils.getPaddedReferenceLength(cigarString);
+        int alignmentEnd = alignmentStart + len - 1;
+
+        return new Locus(chr, alignmentStart, alignmentEnd);
+
+    }
+
+    public Class getFeatureType() {
+        return SAMCodec.class;
+    }
+
+    public Object readHeader(LineReader reader) {
+        return null;
+    }
+
+    boolean isMapped(String[] fields) {
+        int flags = Integer.parseInt(fields[FLAG_COL]);
+        return (flags & READ_UNMAPPED_FLAG) == 0;
+    }
+}
diff --git a/src/org/broad/igv/track/tribble/TribbleFeatureSource.java b/src/org/broad/igv/track/tribble/TribbleFeatureSource.java
new file mode 100644
index 0000000..e3abf48
--- /dev/null
+++ b/src/org/broad/igv/track/tribble/TribbleFeatureSource.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track.tribble;
+
+import net.sf.samtools.util.CloseableIterator;
+import org.broad.igv.data.DataSource;
+import org.broad.igv.feature.Feature;
+import org.broad.igv.feature.Genome;
+import org.broad.igv.feature.LocusScore;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.tdf.TDFDataSource;
+import org.broad.igv.tdf.TDFReader;
+import org.broad.igv.track.FeatureSource;
+import org.broad.igv.util.ParsingUtils;
+import org.broad.igv.util.RuntimeUtils;
+import org.broad.tribble.FeatureCodec;
+import org.broad.tribble.FeatureReader;
+import org.broad.tribble.readers.BasicFeatureReader;
+import org.broad.tribble.util.CloseableTribbleIterator;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class TribbleFeatureSource implements FeatureSource {
+
+    CachingFeatureReader reader;
+    DataSource coverageSource;
+    boolean isVCF;
+
+    /**
+     * Map of IGV chromosome name -> source name
+     */
+    Map<String, String> chrNameMap = new HashMap();
+    private int binSize;
+    Object header;
+
+    public TribbleFeatureSource(String path) throws IOException {
+
+        FeatureCodec codec = CodecFactory.getCodec(path);
+        isVCF = codec instanceof VCFCodec;
+        BasicFeatureReader basicReader = new BasicFeatureReader(path, codec);
+        header = basicReader.getHeader();
+        initBinSize(basicReader);
+        reader = new CachingFeatureReader(basicReader, getBinSize());
+        init();
+        initCoverageSource(path + ".tdf");
+
+    }
+
+    private void initCoverageSource(String covPath) {
+        if (ParsingUtils.pathExists(covPath)) {
+            TDFReader reader = TDFReader.getReader(covPath);
+            coverageSource = new TDFDataSource(reader, 0, "");
+        }
+    }
+
+    private void init() {
+        Genome genome = ViewContext.getInstance().getGenome();
+        if (genome != null) {
+            Collection<String> seqNames = reader.getSequenceNames();
+            if (seqNames != null)
+                for (String seqName : seqNames) {
+                    String igvChr = genome.getChromosomeAlias(seqName);
+                    if (igvChr != null && !igvChr.equals(seqName)) {
+                        chrNameMap.put(igvChr, seqName);
+                    }
+                }
+        }
+    }
+
+
+    /**
+     * Return features overlapping the query interval
+     *
+     * @param chr
+     * @param start
+     * @param end
+     * @return
+     * @throws IOException
+     */
+    public CloseableIterator<Feature> getFeatures(String chr, int start, int end) throws IOException {
+
+        String seqName = chrNameMap.get(chr);
+        if (seqName == null) seqName = chr;
+        return reader.query(seqName, start, end);
+    }
+
+    /**
+     * Return coverage values overlapping the query interval.   At this time Tribble sources do not provide
+     * coverage values
+     *
+     * @param chr
+     * @param start
+     * @param end
+     * @param zoom
+     * @return
+     */
+    public List<LocusScore> getCoverageScores(String chr, int start, int end, int zoom) {
+        return coverageSource == null ? null :
+                coverageSource.getSummaryScoresForRange(chr, start, end, zoom);
+    }
+
+    public int getBinSize() {
+        return binSize;
+    }
+
+    public void setBinSize(int size) {
+        reader.setTileSize(size);
+    }
+
+    public Object getHeader() {
+        return header;
+    }
+
+    // Estimate a binSize (featureVisibilitWindow) such that each bin contains ~ 10 mbytes of features
+
+    private void initBinSize(FeatureReader reader) {
+
+        CloseableTribbleIterator<org.broad.tribble.Feature> iter = null;
+
+        try {
+            double mem = RuntimeUtils.getAvailableMemory();
+            iter = reader.iterator();
+            if (iter.hasNext()) {
+
+                int nSamples = isVCF ? 100 : 1000;
+                org.broad.tribble.Feature firstFeature = iter.next();
+                org.broad.tribble.Feature lastFeature = iter.next();
+                String chr = firstFeature.getChr();
+                int n = 1;
+                long len = 0;
+                while (iter.hasNext() && n < nSamples) {
+                    org.broad.tribble.Feature f = iter.next();
+                    if (f != null) {
+                        n++;
+                        if (f.getChr().equals(chr)) {
+                            lastFeature = f;
+                        } else {
+                            len += lastFeature.getEnd() - firstFeature.getStart() + 1;
+                            firstFeature = f;
+                            lastFeature = f;
+                            chr = f.getChr();
+                        }
+
+                    }
+                }
+                double dMem = mem - RuntimeUtils.getAvailableMemory();
+                double bytesPerFeature = Math.max(100, dMem / n);
+
+                len += lastFeature.getEnd() - firstFeature.getStart() + 1;
+                double featuresPerBase = ((double) n) / len;
+
+                double targetBinMemory =  10000000;  // 10  mega bytes
+                int maxBS = isVCF ? 1000000 : Integer.MAX_VALUE;
+                int bs = Math.min(maxBS, (int) (targetBinMemory / (bytesPerFeature * featuresPerBase)));
+                binSize = Math.max(100000, bs);
+            } else {
+                binSize = Integer.MAX_VALUE;
+            }
+        } catch (IOException e) {
+            binSize = 1000000;
+        }
+        finally {
+            if (iter != null) {
+                iter.close();
+            }
+        }
+    }
+
+
+}
diff --git a/src/org/broad/igv/track/tribble/UCSCCodec.java b/src/org/broad/igv/track/tribble/UCSCCodec.java
new file mode 100644
index 0000000..70a2325
--- /dev/null
+++ b/src/org/broad/igv/track/tribble/UCSCCodec.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track.tribble;
+
+import org.broad.igv.feature.BasicFeature;
+import org.broad.igv.track.TrackProperties;
+import org.broad.igv.track.TrackType;
+import org.broad.igv.util.ParsingUtils;
+import org.broad.tribble.Feature;
+import org.broad.tribble.exception.CodecLineParsingException;
+import org.broad.tribble.util.LineReader;
+
+import java.io.IOException;
+
+/**
+ * @author jrobinso
+ * @date Aug 5, 2010
+ */
+public abstract class UCSCCodec implements org.broad.tribble.FeatureCodec  {
+    static String[] tokens = new String[50];
+    int startBase = 0;
+    FeatureFileHeader header;
+
+    public Object readHeader(LineReader reader) {
+        String nextLine;
+        header = new FeatureFileHeader();
+        int nLines = 0;
+
+        try {
+            while ((nextLine = reader.readLine()) != null &&
+                    (nextLine.startsWith("#") || nextLine.startsWith("track")) ||
+                    nextLine.startsWith("browser")) {
+                nLines++;
+                if (nextLine.startsWith("#type")) {
+                    String[] tokens = nextLine.split("=");
+                    if (tokens.length > 1) {
+                        try {
+                            header.setTrackType(TrackType.valueOf(tokens[1]));
+                        } catch (Exception e) {
+                            // log.error("Error converting track type: " + tokens[1]);
+                        }
+                    }
+                } else if (nextLine.startsWith("track")) {
+                    TrackProperties tp = new TrackProperties();
+                    ParsingUtils.parseTrackLine(nextLine, tp);
+                    header.setTrackProperties(tp);
+                }
+            }
+            return header;
+        }
+        catch (IOException e) {
+            throw new CodecLineParsingException("Error parsing header", e);
+        }
+    }
+
+    public Feature decodeLoc(String line) {
+        return decode(line);  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public Class getFeatureType() {
+        return BasicFeature.class;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+}
diff --git a/src/org/broad/igv/track/tribble/VCFCodec.java b/src/org/broad/igv/track/tribble/VCFCodec.java
new file mode 100644
index 0000000..e63c66c
--- /dev/null
+++ b/src/org/broad/igv/track/tribble/VCFCodec.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track.tribble;
+
+
+import org.broad.igv.util.ParsingUtils;
+import org.broad.tribble.Feature;
+import org.broad.tribble.FeatureCodec;
+import org.broad.tribble.exception.CodecLineParsingException;
+import org.broad.tribble.util.LineReader;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: May 13, 2010
+ * Time: 9:32:03 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class VCFCodec implements FeatureCodec {
+
+    static String[] buffer = new String[8];
+    List<String> headerStrings = new ArrayList();
+
+
+    public Object readHeader(LineReader reader)  {
+        return null;
+
+    }
+
+    public Class getFeatureType() {
+        return VCFFeature.class;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public Object getHeader(Class clazz) throws ClassCastException {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public Feature decodeLoc(String line) {
+        return decode(line);  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public VCFFeature decode(String line) {
+        if (line.startsWith("#")) {
+            return null;
+        }
+
+        //    public VCFFeature(String chr, int start, String id, String ref, String alt, float quality, String filter, String info) {
+
+        int nTokens = ParsingUtils.split(line, buffer, '\t');
+        if (nTokens >= 8) {
+            String chr = buffer[0];
+            Integer start = Integer.parseInt(buffer[1]) - 1;
+            String quality = buffer[5];
+
+            return new VCFFeature(chr, start, buffer[2], buffer[3], buffer[4], quality, buffer[6], buffer[7]);
+        }
+        return null;
+
+    }
+
+
+}
diff --git a/src/org/broad/igv/track/tribble/VCFFeature.java b/src/org/broad/igv/track/tribble/VCFFeature.java
new file mode 100644
index 0000000..a661683
--- /dev/null
+++ b/src/org/broad/igv/track/tribble/VCFFeature.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.track.tribble;
+
+import org.broad.igv.feature.Exon;
+import org.broad.igv.feature.Feature;
+import org.broad.igv.feature.LocusScore;
+import org.broad.igv.feature.Strand;
+import org.broad.igv.track.WindowFunction;
+
+import java.awt.*;
+import java.util.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: May 13, 2010
+ * Time: 9:38:33 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class VCFFeature implements Feature, org.broad.tribble.Feature {
+
+    private String chr;
+    private int start;
+    private String id = "";
+    private String name;
+    private String ref;
+    private String alt;
+    private String quality;
+    private String filter;
+    private String info;
+
+    public VCFFeature(String chr, int start, String id, String ref, String alt, String quality, String filter, String info) {
+        this.chr =  chr;
+        this.start = start;
+        this.id = id;
+        this.ref = ref;
+        this.alt = alt;
+        this.name = "";
+        this.quality = quality;
+        this.filter = filter;
+        this.info = info.replace(";", "<br>");
+    }
+
+    public float getScore() {
+        return 1.0f;
+    }
+
+    public void setConfidence(float confidence) {
+        throw new java.lang.UnsupportedOperationException("setConfidence");
+    }
+
+    public float getConfidence() {
+        return 1.0f;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+
+    public String getValueString(double position, WindowFunction windowFunction) {
+        StringBuffer buf = new StringBuffer();
+        if (id != null && id.length() > 0) {
+            buf.append(id + "<br>");
+        }
+        buf.append(ref + "->" + alt +  "<br>");
+        buf.append("QUAL = " + quality + "<br>");
+        buf.append("FILTER = " + filter + "<br>");
+        buf.append("--------------<br>");
+        buf.append(info);
+        return buf.toString();
+
+    }
+
+    public int getStart() {
+        return start;
+    }
+
+    public void setStart(int start) {
+        this.start = start;
+    }
+
+    public int getEnd() {
+        return start + 1;
+    }
+
+    public String getIdentifier() {
+        return id;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public String getName() {
+        return name;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public String getDescription() {
+        return name;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean hasScore() {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public LocusScore copy() {
+        throw new java.lang.UnsupportedOperationException("Copy not supported");
+    }
+
+    public Strand getStrand() {
+        return Strand.NONE;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean hasStrand() {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public void setName(String name) {
+        throw new java.lang.UnsupportedOperationException("Copy not supported");
+    }
+
+    public Color getColor() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean contains(Feature feature) {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public java.util.List<Exon> getExons() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public int getLength() {
+        return 1;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public Map<String, String> getAttributes() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean contains(double location) {
+        return location >= start && location < start + 1;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public String getURL() {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public void setEnd(int end) {
+        throw new java.lang.UnsupportedOperationException("setEnd not supported");
+
+    }
+
+    public String getType() {
+        return "VCF";  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public String getChr() {
+        return chr;
+    }
+
+    public void setChr(String chr) {
+        throw new java.lang.UnsupportedOperationException("setEnd not supported");
+    }
+
+    public String getQuality() {
+        return quality;
+    }
+
+
+    public String getFilter() {
+        return filter;
+    }
+
+    public String getInfo() {
+        return info;
+    }
+
+
+    // TODO -- move this up
+    public String getLocusString() {
+        return getChr() + ":" + getStart() + ":" + getEnd();
+    }
+
+}
diff --git a/src/org/broad/igv/ui/AboutDialog.java b/src/org/broad/igv/ui/AboutDialog.java
index d5ac0aa..c9637a7 100644
--- a/src/org/broad/igv/ui/AboutDialog.java
+++ b/src/org/broad/igv/ui/AboutDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -24,9 +24,7 @@
 package org.broad.igv.ui;
 
 import org.apache.log4j.Logger;
-
-import java.io.IOException;
-import java.util.Properties;
+import org.broad.igv.Globals;
 
 /**
  * @author jrobinso
@@ -43,26 +41,7 @@ public class AboutDialog extends javax.swing.JDialog {
         initComponents();
     }
 
-    final public static String VERSION;
-    final public static String BUILD;
-    final public static String TIMESTAMP;
-
-    static {
-        Properties properties = new Properties();
-        try {
-            properties.load(AboutDialog.class.getResourceAsStream("/resources/about.properties"));
-        }
-        catch (IOException e) {
-            logger.error("*** Error retrieving version and build information! ***", e);
-        }
-        VERSION = properties.getProperty("version", "???");
-        BUILD = properties.getProperty("build", "???");
-        TIMESTAMP = properties.getProperty("timestamp", "???");
-    }
 
-    private static String versionString() {
-        return "<html>Version " + VERSION + " (" + BUILD + ")<br>" + TIMESTAMP;
-    }
 
     /**
      * This method is called from within the constructor to
@@ -83,7 +62,7 @@ public class AboutDialog extends javax.swing.JDialog {
         jLabel1.setFont(new java.awt.Font("Lucida Grande", 0, 14));
         jLabel1.setText("<html><b>Integrative Genomics Viewer<");
 
-        jLabel2.setText(versionString());
+        jLabel2.setText(Globals.versionString());
 
         okButton.setText("OK");
         okButton.addActionListener(new java.awt.event.ActionListener() {
diff --git a/src/org/broad/igv/ui/AttributeCheckList.java b/src/org/broad/igv/ui/AttributeCheckList.java
index d89f661..04ff421 100644
--- a/src/org/broad/igv/ui/AttributeCheckList.java
+++ b/src/org/broad/igv/ui/AttributeCheckList.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -36,9 +36,6 @@ public class AttributeCheckList extends CheckList {
         super(defaultState);
     }
 
-    public HashSet<String> getSelectedAttributes() {
-        return getSelectedItems();
-    }
 
     public HashSet<String> getUnselectedAttributes() {
         return getUnselectedItems();
diff --git a/src/org/broad/igv/ui/AttributeGroupByDialog.java b/src/org/broad/igv/ui/AttributeGroupByDialog.java
deleted file mode 100644
index e4ed318..0000000
--- a/src/org/broad/igv/ui/AttributeGroupByDialog.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-package org.broad.igv.ui;
-
-import com.jidesoft.dialog.ButtonPanel;
-import com.jidesoft.dialog.StandardDialog;
-import com.jidesoft.swing.JideScrollPane;
-import org.broad.igv.track.AttributeManager;
-
-import javax.swing.*;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.util.HashMap;
-import java.util.Map;
-
-public class AttributeGroupByDialog extends StandardDialog {
-
-    // private JButton okButton = new JButton();
-    private Map<String, JRadioButton> buttons;
-    boolean cancelled = false;
-    String groupByAttribute;
-
-    public AttributeGroupByDialog(JFrame parent) {
-        super(parent);
-        setSize(500, 400);
-    }
-
-    public String getGroupByAttribute() {
-        return groupByAttribute;
-    }
-
-    @Override
-    public JComponent createBannerPanel() {
-        return new JPanel();
-    }
-
-    @Override
-    public JComponent createContentPanel() {
-        JPanel contentPane = new JPanel();
-        JideScrollPane scrollPane = new JideScrollPane(contentPane);
-
-        contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
-        buttons = new HashMap();
-
-        // Group the radio buttons.
-        ButtonGroup group = new ButtonGroup();
-
-        JRadioButton none = new JRadioButton("None");
-        group.add(none);
-        none.addActionListener(new ActionListener() {
-
-            public void actionPerformed(ActionEvent e) {
-                groupByAttribute = null;
-            }
-        });
-        contentPane.add(none);
-
-        for (String attributeName : AttributeManager.getInstance().getAttributeKeys()) {
-            JRadioButton button = new JRadioButton(attributeName);
-            button.addActionListener(new ActionListener() {
-
-                public void actionPerformed(ActionEvent e) {
-                    if (((JRadioButton) e.getSource()).isSelected()) {
-                        groupByAttribute = e.getActionCommand();
-                    }
-                }
-            });
-            buttons.put(attributeName, button);
-            group.add(button);
-            contentPane.add(button);
-        }
-
-        return scrollPane;
-    }
-
-    public boolean isCancelled() {
-        return cancelled;
-    }
-
-    @Override
-    public ButtonPanel createButtonPanel() {
-        ButtonPanel buttonPanel = new ButtonPanel();
-
-        JButton okButton = new JButton("OK");
-        okButton.addActionListener(new ActionListener() {
-
-            public void actionPerformed(ActionEvent e) {
-                setVisible(false);
-            }
-        });
-
-        JButton cancelButton = new JButton();
-        cancelButton.setText("Cancel");
-        cancelButton.addActionListener(new ActionListener() {
-
-            public void actionPerformed(ActionEvent e) {
-                cancelled = true;
-                setVisible(false);
-            }
-        });
-
-        buttonPanel.addButton(okButton, ButtonPanel.AFFIRMATIVE_BUTTON);
-        buttonPanel.addButton(cancelButton, ButtonPanel.CANCEL_BUTTON);
-        return buttonPanel;
-    }
-
-}
diff --git a/src/org/broad/igv/ui/AttributeSelectionDialog.java b/src/org/broad/igv/ui/AttributeSelectionDialog.java
index f243202..5781ad1 100644
--- a/src/org/broad/igv/ui/AttributeSelectionDialog.java
+++ b/src/org/broad/igv/ui/AttributeSelectionDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -48,10 +48,6 @@ public class AttributeSelectionDialog extends javax.swing.JDialog {
         comboBox.setModel(model);
     }
 
-    public String getSelectedItem() {
-        return (String) comboBox.getSelectedItem();
-    }
-
     public void setSelectedItem(Object selection) {
         comboBox.setSelectedItem(selection);
     }
@@ -68,14 +64,6 @@ public class AttributeSelectionDialog extends javax.swing.JDialog {
         return isCanceled;
     }
 
-    public void setCancelingEnabled(boolean isEnabled) {
-        cancelButton.setEnabled(isEnabled);
-    }
-
-    public void setDefaultGenome(GenomeDescriptor genome) {
-        comboBox.setSelectedItem(genome);
-    }
-
     /**
      * This method is called from within the constructor to
      * initialize the form.
diff --git a/src/org/broad/igv/ui/DataRangeDialog.java b/src/org/broad/igv/ui/DataRangeDialog.java
index 19e8922..e9dbd48 100644
--- a/src/org/broad/igv/ui/DataRangeDialog.java
+++ b/src/org/broad/igv/ui/DataRangeDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/DefaultExceptionHandler.java b/src/org/broad/igv/ui/DefaultExceptionHandler.java
index 194510d..79ba033 100644
--- a/src/org/broad/igv/ui/DefaultExceptionHandler.java
+++ b/src/org/broad/igv/ui/DefaultExceptionHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/FontManager.java b/src/org/broad/igv/ui/FontManager.java
index 3a1856a..225e97b 100644
--- a/src/org/broad/igv/ui/FontManager.java
+++ b/src/org/broad/igv/ui/FontManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/GenomeBuilderDialog.java b/src/org/broad/igv/ui/GenomeBuilderDialog.java
index 9c4d4dd..08796bb 100644
--- a/src/org/broad/igv/ui/GenomeBuilderDialog.java
+++ b/src/org/broad/igv/ui/GenomeBuilderDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -18,7 +18,6 @@
 
 package org.broad.igv.ui;
 
-import org.broad.igv.IGVConstants;
 import org.broad.igv.ui.util.OkCancelDialog;
 
 import javax.swing.*;
@@ -104,7 +103,7 @@ public class GenomeBuilderDialog extends OkCancelDialog {
         // Passed validation now get genome location and check it
         if (isOk) {
 
-            if (IGVConstants.IS_MAC) {
+            if (UIConstants.IS_MAC) {
                 genomeArchiveFile = builderPane.showGenomeArchiveDirectoryChooser();
             } else {
                 genomeArchiveFile = builderPane.showGenomeArchiveDirectoryChooser();
diff --git a/src/org/broad/igv/ui/GenomeBuilderPane.java b/src/org/broad/igv/ui/GenomeBuilderPane.java
index d3901df..f6ab9b6 100644
--- a/src/org/broad/igv/ui/GenomeBuilderPane.java
+++ b/src/org/broad/igv/ui/GenomeBuilderPane.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -18,7 +18,7 @@
 package org.broad.igv.ui;
 
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
+import org.broad.igv.Globals;
 import org.broad.igv.PreferenceManager;
 import org.broad.igv.feature.CytoBandFileParser;
 import org.broad.igv.feature.GeneManager;
@@ -105,7 +105,7 @@ public class GenomeBuilderPane extends javax.swing.JPanel {
     public String getArchiveFileName() {
 
         if (genomeFilename == null) {
-            genomeFilename = getGenomeId() + IGVConstants.GENOME_FILE_EXTENSION;
+            genomeFilename = getGenomeId() + Globals.GENOME_FILE_EXTENSION;
         }
         return genomeFilename;
     }
@@ -314,7 +314,7 @@ public class GenomeBuilderPane extends javax.swing.JPanel {
         directory = PreferenceManager.getInstance().getLastGenomeImportDirectory();
         File newArchiveFile = new File(directory, getArchiveFileName());
 
-        String extensions[] = {IGVConstants.GENOME_FILE_EXTENSION};
+        String extensions[] = {Globals.GENOME_FILE_EXTENSION};
         File file = null;
         boolean isOk = false;
         if (!isOk) {
@@ -331,8 +331,8 @@ public class GenomeBuilderPane extends javax.swing.JPanel {
 
                 genomeFilename = file.getName();
                 if (genomeFilename != null) {
-                    if (!genomeFilename.endsWith(IGVConstants.GENOME_FILE_EXTENSION)) {
-                        genomeFilename += IGVConstants.GENOME_FILE_EXTENSION;
+                    if (!genomeFilename.endsWith(Globals.GENOME_FILE_EXTENSION)) {
+                        genomeFilename += Globals.GENOME_FILE_EXTENSION;
                     }
                     genomeArchiveLocation = file.getParentFile().getAbsolutePath();
                     isOk = true;
diff --git a/src/org/broad/igv/ui/GenomeSelectionDialog.java b/src/org/broad/igv/ui/GenomeSelectionDialog.java
index 08b712a..43c11fa 100644
--- a/src/org/broad/igv/ui/GenomeSelectionDialog.java
+++ b/src/org/broad/igv/ui/GenomeSelectionDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/GuiUtilities.java b/src/org/broad/igv/ui/GuiUtilities.java
deleted file mode 100644
index a82bcf8..0000000
--- a/src/org/broad/igv/ui/GuiUtilities.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-
-package org.broad.igv.ui;
-
-//~--- JDK imports ------------------------------------------------------------
-
-import javax.swing.*;
-
-
-/**
- * @author jrobinso
- */
-public class GuiUtilities {
-
-    /**
-     * A wrapper around invokeOnEventThread.  If the runnable is already in the event dispatching
-     * queue it is just run.  Otherwise it is placed in the queue via invokeOnEventThread.
-     * <p/>
-     * I'm not sure this is strictly neccessary,  but is safe.
-     *
-     * @param runnable
-     */
-    public static void invokeOnEventThread(Runnable runnable) {
-        if (SwingUtilities.isEventDispatchThread()) {
-            runnable.run();
-        } else {
-            SwingUtilities.invokeLater(runnable);
-        }
-
-    }
-
-}
diff --git a/src/org/broad/igv/ui/HeatmapDialog.form b/src/org/broad/igv/ui/HeatmapDialog.form
new file mode 100644
index 0000000..8ae294e
--- /dev/null
+++ b/src/org/broad/igv/ui/HeatmapDialog.form
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.broad.igv.ui.HeatmapDialog">
+  <grid id="cbd77" binding="contentPane" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="10" left="10" bottom="10" right="10"/>
+    <constraints>
+      <xy x="48" y="54" width="436" height="297"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <grid id="94766" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <hspacer id="98af6">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+            </constraints>
+          </hspacer>
+          <grid id="9538f" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="true" same-size-vertically="false" hgap="-1" vgap="-1">
+            <margin top="0" left="0" bottom="0" right="0"/>
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="e7465" class="javax.swing.JButton" binding="buttonOK">
+                <constraints>
+                  <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+                </constraints>
+                <properties>
+                  <text value="OK"/>
+                </properties>
+              </component>
+              <component id="5723f" class="javax.swing.JButton" binding="buttonCancel">
+                <constraints>
+                  <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+                </constraints>
+                <properties>
+                  <text value="Cancel"/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+        </children>
+      </grid>
+      <grid id="e3588" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <hspacer id="3e33a">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+            </constraints>
+          </hspacer>
+          <vspacer id="b7e94">
+            <constraints>
+              <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+            </constraints>
+          </vspacer>
+          <component id="a375b" class="javax.swing.JComboBox">
+            <constraints>
+              <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+          </component>
+        </children>
+      </grid>
+    </children>
+  </grid>
+</form>
diff --git a/src/org/broad/igv/ui/HeatmapDialog.java b/src/org/broad/igv/ui/HeatmapDialog.java
new file mode 100644
index 0000000..abd2aba
--- /dev/null
+++ b/src/org/broad/igv/ui/HeatmapDialog.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.ui;
+
+import javax.swing.*;
+import java.awt.event.*;
+
+public class HeatmapDialog extends JDialog {
+    private JPanel contentPane;
+    private JButton buttonOK;
+    private JButton buttonCancel;
+
+    public HeatmapDialog() {
+        setContentPane(contentPane);
+        setModal(true);
+        getRootPane().setDefaultButton(buttonOK);
+
+        buttonOK.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                onOK();
+            }
+        });
+
+        buttonCancel.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                onCancel();
+            }
+        });
+
+// call onCancel() when cross is clicked
+        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+        addWindowListener(new WindowAdapter() {
+            public void windowClosing(WindowEvent e) {
+                onCancel();
+            }
+        });
+
+// call onCancel() on ESCAPE
+        contentPane.registerKeyboardAction(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                onCancel();
+            }
+        }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+    }
+
+    private void onOK() {
+// add your code here
+        dispose();
+    }
+
+    private void onCancel() {
+// add your code here if necessary
+        dispose();
+    }
+
+    public static void main(String[] args) {
+        HeatmapDialog dialog = new HeatmapDialog();
+        dialog.pack();
+        dialog.setVisible(true);
+        System.exit(0);
+    }
+}
diff --git a/src/org/broad/igv/ui/HeatmapScaleDialog.java b/src/org/broad/igv/ui/HeatmapScaleDialog.java
index c1f2cfc..d5ffa3f 100644
--- a/src/org/broad/igv/ui/HeatmapScaleDialog.java
+++ b/src/org/broad/igv/ui/HeatmapScaleDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/IGVCommandBar.java b/src/org/broad/igv/ui/IGVCommandBar.java
index 54b44e1..7f5ed3f 100644
--- a/src/org/broad/igv/ui/IGVCommandBar.java
+++ b/src/org/broad/igv/ui/IGVCommandBar.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,22 +22,25 @@
  */
 package org.broad.igv.ui;
 
-//~--- non-JDK imports --------------------------------------------------------
 
 import com.jidesoft.swing.JideBoxLayout;
 import com.jidesoft.swing.JideButton;
 import com.jidesoft.swing.JideToggleButton;
 import com.jidesoft.utils.SwingWorker;
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
+import org.broad.igv.Globals;
 import org.broad.igv.PreferenceManager;
 import org.broad.igv.feature.GenomeManager;
 import org.broad.igv.feature.GenomeManager.GenomeListItem;
 import org.broad.igv.feature.GenomeServerException;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.ui.action.SearchCommand;
 import org.broad.igv.ui.util.IconFactory;
 import org.broad.igv.ui.util.ProgressBar;
 import org.broad.igv.ui.util.ProgressMonitor;
+import org.broad.igv.ui.util.UIUtilities;
+import org.broad.igv.util.LRUCache;
+import org.broad.igv.util.NamedRunnable;
 
 import javax.swing.*;
 import javax.swing.border.EmptyBorder;
@@ -52,7 +55,6 @@ import java.io.FileNotFoundException;
 import java.net.NoRouteToHostException;
 import java.util.*;
 import java.util.List;
-import java.util.concurrent.ExecutionException;
 
 /**
  * @author jrobinso
@@ -75,7 +77,9 @@ public class IGVCommandBar extends javax.swing.JPanel {
     private javax.swing.JPanel toolPanel;
     private javax.swing.JPanel zoomControl;
     private ItemListener genomeComboBoxItemListener = null;
-    final private int DEFAULT_CHROMOSOME_DROPDOWN_WIDTH = 80;
+    final private int DEFAULT_CHROMOSOME_DROPDOWN_WIDTH = 120;
+    private JideButton backButton;
+    private JideButton forwardButton;
 
     /**
      * Creates new form IGVCommandBar
@@ -96,7 +100,7 @@ public class IGVCommandBar extends javax.swing.JPanel {
 
         roiToggleButton.setVisible(roiVisible);
 
-        boolean isWholeGenome = currentChr.equals(IGVConstants.CHR_ALL);
+        boolean isWholeGenome = currentChr.equals(Globals.CHR_ALL);
 
         roiToggleButton.setEnabled(!isWholeGenome);
         zoomControl.setEnabled(!isWholeGenome);
@@ -193,7 +197,7 @@ public class IGVCommandBar extends javax.swing.JPanel {
                     if (previousSelectedItemId != -1) {
 
                         final int index = previousSelectedItemId;
-                        GuiUtilities.invokeOnEventThread(new Runnable() {
+                        UIUtilities.invokeOnEventThread(new Runnable() {
 
                             public void run() {
                                 genomeComboBox.setSelectedIndex(index);
@@ -219,7 +223,7 @@ public class IGVCommandBar extends javax.swing.JPanel {
                             if (item instanceof GenomeListItem) {
                                 GenomeListItem genomeListItem = (GenomeListItem) item;
                                 String requestedGenomeId = genomeListItem.getId();
-                                String currentGenomeId = IGVModel.getInstance().getViewContext().getGenomeId();
+                                String currentGenomeId = ViewContext.getInstance().getGenomeId();
                                 if ((currentGenomeId != null) && requestedGenomeId.equalsIgnoreCase(currentGenomeId)) {
 
                                     // Nothing to do if genome already loaded
@@ -251,14 +255,9 @@ public class IGVCommandBar extends javax.swing.JPanel {
 
                                         previousSelectedItemId = genomeComboBox.getSelectedIndex();
 
-                                        GuiUtilities.invokeOnEventThread(new Runnable() {
-
-                                            public void run() {
-                                                monitor.fireProgressChange(25);
-                                            }
-                                        });
+                                        monitor.fireProgressChange(25);
 
-                                        GuiUtilities.invokeOnEventThread(new Runnable() {
+                                        UIUtilities.invokeOnEventThread(new Runnable() {
 
                                             public void run() {
                                                 if (bar != null) {
@@ -268,18 +267,18 @@ public class IGVCommandBar extends javax.swing.JPanel {
                                         });
                                     } catch (GenomeServerException e) {
 
-                                        GuiUtilities.invokeOnEventThread(new Runnable() {
+                                        UIUtilities.invokeOnEventThread(new Runnable() {
 
                                             public void run() {
                                                 JOptionPane.showMessageDialog(
                                                         IGVMainFrame.getInstance(),
-                                                        IGVConstants.CANNOT_ACCESS_SERVER_GENOME_LIST);
+                                                        UIConstants.CANNOT_ACCESS_SERVER_GENOME_LIST);
                                             }
                                         });
                                         reselectPreviousGenome();
                                         return null;
                                     } catch (FileNotFoundException e) {
-                                        GuiUtilities.invokeOnEventThread(new Runnable() {
+                                        UIUtilities.invokeOnEventThread(new Runnable() {
 
                                             public void run() {
                                                 if (bar != null) {
@@ -361,21 +360,21 @@ public class IGVCommandBar extends javax.swing.JPanel {
                         GenomeManager.getInstance().getServerGenomeArchiveList(excludedArchivesUrls);
             } catch (Exception e) {
 
-                GuiUtilities.invokeOnEventThread(new Runnable() {
+                UIUtilities.invokeOnEventThread(new Runnable() {
 
                     public void run() {
                         JOptionPane.showMessageDialog(
                                 IGVMainFrame.getInstance(),
-                                IGVConstants.CANNOT_ACCESS_SERVER_GENOME_LIST);
+                                UIConstants.CANNOT_ACCESS_SERVER_GENOME_LIST);
                     }
                 });
             }
 
             LinkedHashSet<GenomeListItem> cacheGenomeItemList =
-                    GenomeManager.getInstance().getCachedGenomeArchiveList(excludedArchivesUrls);
+                    GenomeManager.getInstance().getCachedGenomeArchiveList();
 
             LinkedHashSet<GenomeListItem> clientSideItemList =
-                    GenomeManager.getInstance().getUserDefinedGenomeArchiveList(excludedArchivesUrls);
+                    GenomeManager.getInstance().getUserDefinedGenomeArchiveList();
 
             setGenomeItemList(clientSideItemList, serverSideItemList, cacheGenomeItemList);
             setGenomeItemListModel();
@@ -385,29 +384,15 @@ public class IGVCommandBar extends javax.swing.JPanel {
         }
     }
 
-    /**
-     * Method description
-     *
-     * @param value
-     */
-    public void enableROI(boolean value) {
-        roiToggleButton.setVisible(value);
-    }
-
-    /**
-     * Method description
-     *
-     * @param owner
-     */
-    public void setOwner(IGVMainFrame owner) {
-        this.owner = owner;
-    }
 
     void updateChromosomeDropdown() {
 
         List<String> tmp = new LinkedList(getViewContext().getChromosomeNames());
         if (tmp.size() > 1) {
-            tmp.add(IGVConstants.CHR_ALL);
+            String homeChr = getViewContext().getHomeChr();
+            if (homeChr.equals(Globals.CHR_ALL)) {
+                tmp.add(Globals.CHR_ALL);
+            }
         }
 
         Graphics2D graphics2D = (Graphics2D) chromosomeComboBox.getGraphics();
@@ -433,20 +418,20 @@ public class IGVCommandBar extends javax.swing.JPanel {
         final DefaultComboBoxModel defaultModel = new DefaultComboBoxModel(chomosomeNames);
         final int dropdownWidth = w;
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
                 adjustChromosomeDropdownWidth(dropdownWidth);
                 chromosomeComboBox.setModel(defaultModel);
-                chromosomeComboBox.setSelectedItem(getModel().getViewContext().getChrName());
+                chromosomeComboBox.setSelectedItem(getViewContext().getChrName());
             }
         });
 
     }
 
     protected void chromosomeChanged() {
-        roiToggleButton.setEnabled(!getViewContext().getChrName().equals(IGVConstants.CHR_ALL));
-        zoomControl.setEnabled(!getViewContext().getChrName().equals(IGVConstants.CHR_ALL));
+        roiToggleButton.setEnabled(!getViewContext().getChrName().equals(Globals.CHR_ALL));
+        zoomControl.setEnabled(!getViewContext().getChrName().equals(Globals.CHR_ALL));
 
         if (chromosomeComboBox.getSelectedItem() != null) {
             if (!chromosomeComboBox.getSelectedItem().equals(getViewContext().getChrName())) {
@@ -471,11 +456,11 @@ public class IGVCommandBar extends javax.swing.JPanel {
 
         String p = "";
 
-        if (!chr.equals(IGVConstants.CHR_ALL)) {
-            p = getViewContext().getCurrentLocusString();
+        if (!chr.equals(Globals.CHR_ALL)) {
+            p = getViewContext().getFormattedLocusString();
         }
         final String position = p;
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
                 getSearchTextField().setText(position);
@@ -497,12 +482,8 @@ public class IGVCommandBar extends javax.swing.JPanel {
         return searchTextField;
     }
 
-    private IGVModel getModel() {
-        return owner.getModel();
-    }
-
     private ViewContext getViewContext() {
-        return getModel().getViewContext();
+        return ViewContext.getInstance();
     }
 
     static class ComboBoxRenderer implements ListCellRenderer {
@@ -532,11 +513,11 @@ public class IGVCommandBar extends javax.swing.JPanel {
 
             Component renderer = null;
 
-            if (IGVConstants.GENOME_LIST_SEPARATOR.equals(text)) {
+            if (UIConstants.GENOME_LIST_SEPARATOR.equals(text)) {
                 return separator;
             }
 
-            if (text.equals(IGVConstants.REMOVE_GENOME_LIST_MENU_ITEM)) {
+            if (text.equals(UIConstants.REMOVE_GENOME_LIST_MENU_ITEM)) {
                 JLabel label = new JLabel(text);
 
                 label.setOpaque(true);
@@ -656,20 +637,19 @@ public class IGVCommandBar extends javax.swing.JPanel {
     /**
      * Called from session loading,  command line listener, startup code
      */
-    public void selectGenomeFromList(final String gid)
+    public void selectGenomeFromList(final String genomeId)
             throws FileNotFoundException, NoRouteToHostException {
 
 
         // See if this genome is already loaded
-        String currentGenomeId = IGVModel.getInstance().getViewContext().getGenomeId();
-        if (currentGenomeId != null && gid != null && gid.equalsIgnoreCase(currentGenomeId)) {
+        String currentGenomeId = ViewContext.getInstance().getGenomeId();
+        if (currentGenomeId != null && genomeId != null && genomeId.equalsIgnoreCase(currentGenomeId)) {
             return;
         }
 
-        final String genomeId = checkGenomeId(gid);
 
 
-        Runnable runnable = new Runnable() {
+        NamedRunnable runnable = new NamedRunnable() {
 
             public void run() {
 
@@ -695,6 +675,11 @@ public class IGVCommandBar extends javax.swing.JPanel {
                                 // process it
                                 if ((id != null) && id.trim().equalsIgnoreCase(genomeId)) {
 
+
+                                    if (log.isDebugEnabled()) {
+                                        log.debug("Call loadGenome");
+                                    }
+
                                     GenomeManager.getInstance().loadGenome(
                                             genomeListItem.getLocation(),
                                             genomeListItem.isUserDefined(),
@@ -743,19 +728,19 @@ public class IGVCommandBar extends javax.swing.JPanel {
                 log.debug("Finish selectGenomeFromList");
 
             }
-        };
 
-        if (SwingUtilities.isEventDispatchThread()) {
-            try {
-                LongRunningTask.submit(runnable).get();
-            } catch (InterruptedException ex) {
-                log.error(ex);
-            } catch (ExecutionException ex) {
-                log.error(ex);
+            public String getName() {
+                return "selectGenomeFromList: " + genomeId;  //To change body of implemented methods use File | Settings | File Templates.
             }
-        } else {
+        };
+
+        WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
+        try {
             runnable.run();
         }
+        finally {
+            WaitCursorManager.removeWaitCursor(token);
+        }
     }
 
     /**
@@ -767,29 +752,33 @@ public class IGVCommandBar extends javax.swing.JPanel {
 
         LinkedHashSet<Object> list = new LinkedHashSet();
 
-        if ((this.userDefinedGenomeItemList != null) && !this.userDefinedGenomeItemList.isEmpty()) {
-            for (GenomeListItem item : this.userDefinedGenomeItemList) {
+        if ((userDefinedGenomeItemList != null) && !userDefinedGenomeItemList.isEmpty()) {
+            for (GenomeListItem item : userDefinedGenomeItemList) {
                 list.add(item);
             }
-            list.add(IGVConstants.GENOME_LIST_SEPARATOR);
+            list.add(UIConstants.GENOME_LIST_SEPARATOR);
         }
 
-        if ((this.serverGenomeItemList != null) && !this.serverGenomeItemList.isEmpty()) {
+        if ((serverGenomeItemList != null) && !serverGenomeItemList.isEmpty()) {
             for (GenomeListItem item : this.serverGenomeItemList) {
                 list.add(item);
             }
 
-            if ((this.cachedGenomeItemList == null) || this.cachedGenomeItemList.isEmpty()) {
-                list.add(IGVConstants.GENOME_LIST_SEPARATOR);
+            if ((cachedGenomeItemList == null) || cachedGenomeItemList.isEmpty()) {
+                list.add(UIConstants.GENOME_LIST_SEPARATOR);
             }
         }
 
-        if ((this.cachedGenomeItemList != null) && !this.cachedGenomeItemList.isEmpty()) {
+        if ((cachedGenomeItemList != null) && !cachedGenomeItemList.isEmpty()) {
             for (GenomeListItem item : this.cachedGenomeItemList) {
                 list.add(item);
             }
 
-            list.add(IGVConstants.GENOME_LIST_SEPARATOR);
+            list.add(UIConstants.GENOME_LIST_SEPARATOR);
+        }
+
+        if(list.isEmpty()) {
+            list.add(GenomeManager.getInstance().getDefaultGenomeListItem());
         }
 
         return new DefaultComboBoxModel(list.toArray());
@@ -821,7 +810,7 @@ public class IGVCommandBar extends javax.swing.JPanel {
         }
 
         // If the genome has not been set user the one at the top of the list
-        /*String genome = IGVModel.getInstance().getViewContext().getGenomeId();
+        /*String genome = ViewContext.getInstance().getGenomeId();
         if (genome == null) {
         for (GenomeListItem item : mergedServerAndCacheItemList) {
 
@@ -869,12 +858,7 @@ public class IGVCommandBar extends javax.swing.JPanel {
             cachedGenomeItemList = new LinkedHashSet<GenomeListItem>();
         }
 
-        /*if (clientItemList.isEmpty() && serverItemList.isEmpty() && cachedGenomeItemList.isEmpty()) {
-            JOptionPane.showMessageDialog(IGVMainFrame.getInstance(),
-                    "No genomes founds during configuration!");
-            throw new RuntimeException("No genomes founds during configuration!");
-        } */
-
+       
         this.userDefinedGenomeItemList = clientItemList;
         this.cachedGenomeItemList = cachedGenomeItemList;
         this.serverGenomeItemList = serverItemList;
@@ -927,7 +911,7 @@ public class IGVCommandBar extends javax.swing.JPanel {
 
                     // Only allow selection of genomes
                     super.setSelectedIndex(index);
-                } else if (text.equals(IGVConstants.LOAD_GENOME_LIST_MENU_ITEM)) {
+                } else if (text.equals(UIConstants.LOAD_GENOME_LIST_MENU_ITEM)) {
                     IGVMainFrame.getInstance().doLoadGenome(null);
                 }
             }
@@ -939,12 +923,9 @@ public class IGVCommandBar extends javax.swing.JPanel {
 
         chromosomeComboBox = new javax.swing.JComboBox();
         chromosomeComboBox.setToolTipText("Select a chromosome to view");
-        chromosomeComboBox.setMaximumSize(new java.awt.Dimension(DEFAULT_CHROMOSOME_DROPDOWN_WIDTH,
-                35));
-        chromosomeComboBox.setMinimumSize(new java.awt.Dimension(DEFAULT_CHROMOSOME_DROPDOWN_WIDTH,
-                27));
-        chromosomeComboBox.setPreferredSize(
-                new java.awt.Dimension(DEFAULT_CHROMOSOME_DROPDOWN_WIDTH, 16));
+        chromosomeComboBox.setMaximumSize(new java.awt.Dimension(DEFAULT_CHROMOSOME_DROPDOWN_WIDTH, 35));
+        chromosomeComboBox.setMinimumSize(new java.awt.Dimension(DEFAULT_CHROMOSOME_DROPDOWN_WIDTH, 27));
+        chromosomeComboBox.setPreferredSize(new java.awt.Dimension(DEFAULT_CHROMOSOME_DROPDOWN_WIDTH, 16));
         chromosomeComboBox.addActionListener(new java.awt.event.ActionListener() {
 
             public void actionPerformed(java.awt.event.ActionEvent evt) {
@@ -955,8 +936,7 @@ public class IGVCommandBar extends javax.swing.JPanel {
         locationPanel.add(Box.createHorizontalStrut(10), JideBoxLayout.FIX);
 
         searchTextField = new SearchTextField();
-        searchTextField.setToolTipText(
-                "Enter a gene of locus, e.f. EGFR,   chr1,   or chr1:100,000-200,000");
+        searchTextField.setToolTipText("Enter a gene of locus, e.f. EGFR,   chr1,   or chr1:100,000-200,000");
         searchTextField.setMaximumSize(new java.awt.Dimension(250, 15));
         searchTextField.setMinimumSize(new java.awt.Dimension(100, 28));
         searchTextField.setPreferredSize(new java.awt.Dimension(250, 28));
@@ -990,6 +970,40 @@ public class IGVCommandBar extends javax.swing.JPanel {
 
         // toolPanel.setBorder(
         // new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
+/*        backButton = new JideButton();
+        backButton.setIcon(
+                new javax.swing.ImageIcon(
+                        getClass().getResource("/toolbarButtonGraphics/navigation/Back24.gif")));    // NOI18N
+
+        backButton.setToolTipText("Back");
+        backButton.addActionListener(new java.awt.event.ActionListener() {
+
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                String locus = ViewContext.getInstance().history.back();
+                if (locus != null) {
+                    (new SearchCommand(ViewContext.getInstance(), locus, false)).execute();
+                }
+
+            }
+        });
+        toolPanel.add(backButton, JideBoxLayout.FIX);
+
+        forwardButton = new JideButton();
+        forwardButton.setIcon(
+                new javax.swing.ImageIcon(
+                        getClass().getResource("/toolbarButtonGraphics/navigation/Forward24.gif")));    // NOI18N
+        forwardButton.setToolTipText("Forward");
+        forwardButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                String locus = ViewContext.getInstance().history.forward();
+                if (locus != null) {
+                    (new SearchCommand(ViewContext.getInstance(), locus, false)).execute();
+                }
+
+            }
+        });
+        toolPanel.add(forwardButton, JideBoxLayout.FIX);
+*/
 
         homeButton = new com.jidesoft.swing.JideButton();
         homeButton.setAlignmentX(RIGHT_ALIGNMENT);
@@ -1006,6 +1020,7 @@ public class IGVCommandBar extends javax.swing.JPanel {
         });
         toolPanel.add(homeButton, JideBoxLayout.FIX);
 
+
         refreshButton = new com.jidesoft.swing.JideButton();
         refreshButton.setAlignmentX(RIGHT_ALIGNMENT);
         refreshButton.setIcon(
@@ -1078,6 +1093,7 @@ public class IGVCommandBar extends javax.swing.JPanel {
     private void homeButtonActionPerformed(java.awt.event.ActionEvent evt) {
         String chr = getViewContext().getHomeChr();
         getViewContext().setChromosomeName(chr);
+        getViewContext().history.push(chr);
         chromosomeComboBox.setSelectedItem(chr);
         updateCurrentCoordinates();
         owner.chromosomeChangeEvent();
@@ -1085,18 +1101,21 @@ public class IGVCommandBar extends javax.swing.JPanel {
     }
 
     private void refreshButtonActionPerformed(java.awt.event.ActionEvent evt) {
-        owner.doRefreshAndImageCacheClear();
+        LRUCache.clearCaches();
+        owner.doRefresh();
     }
 
     private void chromosomeComboBoxActionPerformed(java.awt.event.ActionEvent evt) {
         javax.swing.JComboBox combobox = ((javax.swing.JComboBox) evt.getSource());
         String chromosomeName = (String) combobox.getSelectedItem();
-
-        getViewContext().setChromosomeName(chromosomeName);
-        updateCurrentCoordinates();
-        owner.chromosomeChangeEvent();
-        owner.repaint();
-        PreferenceManager.getInstance().setLastChromosomeViewed(chromosomeName);
+        if (chromosomeName != null  && !chromosomeName.equals(getViewContext().getChrName())) {
+            getViewContext().setChromosomeName(chromosomeName);
+            getViewContext().recordHistory();
+            updateCurrentCoordinates();
+            owner.chromosomeChangeEvent();
+            owner.repaint();
+            PreferenceManager.getInstance().setLastChromosomeViewed(chromosomeName);
+        }
     }
 
     private void goButtonActionPerformed(java.awt.event.ActionEvent evt) {    // GEN-FIRST:event_goButtonActionPerformed
@@ -1107,23 +1126,20 @@ public class IGVCommandBar extends javax.swing.JPanel {
 
 
     public void searchByLocus(String searchText) {
-        searchByLocus(searchText, true);
-    }
 
-    public void searchByLocus(String searchText, boolean async) {
 
         if (log.isDebugEnabled()) {
-            log.debug("Enter search by locus: " + searchText + " (async=" + async + ")");
+            log.debug("Enter search by locus: " + searchText);
         }
 
         if ((searchText != null) && (searchText.length() > 0)) {
             getSearchTextField().setText(searchText);
-            (new SearchCommand(getModel().getViewContext(), searchText)).execute(async);
-            chromosomeComboBox.setSelectedItem(getModel().getViewContext().getChrName());
+            (new SearchCommand(getViewContext(), searchText)).execute();
+            chromosomeComboBox.setSelectedItem(getViewContext().getChrName());
         }
 
         if (log.isDebugEnabled()) {
-            log.debug("Exit search by locus: " + searchText + " (async=" + async + ")");
+            log.debug("Exit search by locus: " + searchText);
         }
     }
 
@@ -1153,30 +1169,4 @@ public class IGVCommandBar extends javax.swing.JPanel {
     }
 
 
-    private String checkGenomeId(String genomeId) {
-        // See if the genomeID is available
-        Map<String, String> aliasTable = GenomeManager.getInstance().getAliasTable();
-
-        if (aliasTable != null && !aliasTable.isEmpty()) {
-
-            int cnt = genomeComboBox.getItemCount();
-            Set<String> availableIds = new HashSet(cnt);
-            for (int i = 0; i < cnt; i++) {
-                Object item = genomeComboBox.getItemAt(i);
-                if (item instanceof GenomeListItem) {
-                    GenomeListItem genomeListItem = (GenomeListItem) item;
-                    availableIds.add(genomeListItem.getId());
-                }
-            }
-            if (!availableIds.contains(genomeId)) {
-                String altGenomeId = aliasTable.get(genomeId);
-                if (altGenomeId != null && availableIds.contains(altGenomeId)) {
-                    return altGenomeId;
-                }
-            }
-        }
-        return genomeId;
-    }
-
-
 }
diff --git a/src/org/broad/igv/ui/IGVMainFrame.java b/src/org/broad/igv/ui/IGVMainFrame.java
index 55dd00a..5b079ec 100644
--- a/src/org/broad/igv/ui/IGVMainFrame.java
+++ b/src/org/broad/igv/ui/IGVMainFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -28,44 +28,44 @@ import com.jidesoft.swing.JideBoxLayout;
 import com.jidesoft.swing.JideScrollPane;
 import com.jidesoft.swing.JideSplitPane;
 import com.jidesoft.utils.SwingWorker;
+import jargs.gnu.CmdLineParser;
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
-import static org.broad.igv.IGVConstants.*;
+import org.broad.igv.Globals;
 import org.broad.igv.PreferenceManager;
 import org.broad.igv.data.IGVDatasetParser;
 import org.broad.igv.event.StatusChangeEvent;
-import org.broad.igv.feature.FeatureDB;
-import org.broad.igv.feature.GenomeDescriptor;
-import org.broad.igv.feature.GenomeManager;
+import org.broad.igv.feature.*;
 import org.broad.igv.feature.GenomeManager.GenomeListItem;
-import org.broad.igv.feature.MaximumContigGenomeException;
 import org.broad.igv.listener.StatusListener;
+import org.broad.igv.main.BatchRunner;
 import org.broad.igv.main.CommandListener;
-import org.broad.igv.roc.ROC;
 import org.broad.igv.session.Session;
-import org.broad.igv.session.SessionManager;
+import org.broad.igv.session.SessionReader;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.tools.IgvToolsGui;
 import org.broad.igv.track.AttributeManager;
+import org.broad.igv.track.Track;
 import org.broad.igv.track.TrackManager;
+
+import static org.broad.igv.ui.UIConstants.*;
 import static org.broad.igv.ui.WaitCursorManager.CursorToken;
+
 import org.broad.igv.ui.action.*;
 import org.broad.igv.ui.dnd.GhostGlassPane;
 import org.broad.igv.ui.event.GlobalKeyDispatcher;
 import org.broad.igv.ui.legend.LegendDialog;
 import org.broad.igv.ui.panel.*;
 import org.broad.igv.ui.util.*;
+
 import static org.broad.igv.ui.util.GenericUtilities.*;
+
 import org.broad.igv.ui.util.ProgressMonitor;
+
 import static org.broad.igv.ui.util.UIUtilities.getFileChooser;
-import static org.broad.igv.ui.util.UIUtilities.showAndLogErrorMessage;
-import org.broad.igv.util.BrowserLauncher;
-import org.broad.igv.util.FileUtils;
-import org.broad.igv.util.ResourceLocator;
-import org.broad.igv.util.Utilities;
-import org.jdesktop.layout.GroupLayout;
+
+import org.broad.igv.util.*;
 
 import javax.swing.*;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
 import javax.swing.filechooser.FileFilter;
 import javax.swing.plaf.basic.BasicBorders;
 import java.awt.*;
@@ -86,87 +86,101 @@ import java.util.List;
  */
 public class IGVMainFrame extends javax.swing.JFrame {
 
-    public static final String DATA_PANEL_NAME = "DataPanel";
-    public static final String FEATURE_PANEL_NAME = "FeaturePanel";
     private static Logger log = Logger.getLogger(IGVMainFrame.class);
     private static IGVMainFrame theInstance;
-    // Object to hold state and reduce bloat of this class
-    private IGVModel model = IGVModel.getInstance();
 
-    // Force constants to load early
-    static {
-        System.out.println(IGVConstants.APPLICATION_LONG_NAME + " starting...");
-    }
+    // Cursors
+    public static Cursor handCursor;
+    public static Cursor fistCursor;
+    public static Cursor zoomInCursor;
+    public static Cursor zoomOutCursor;
+    public static Cursor dragNDropCursor;
 
-    static {
-        FileUtils.addRollingAppenderToRootLogger();
-        log.info("Default User Directory: " + IGVConstants.getDefaultUserDirectory());
-    }
+    final static String ROI_BUTTON_TEXT = "Define a region of interest";
 
+    //Session session;
+    Session session;
     /**
-     * The view context.  Manages location, zoom, chromosome
+     * Helper class for managing tracks
      */
-    private JMenu toolMenu;
+    private TrackManager trackManager;
+
+    // Panels
+    private JMenu extrasMenu;
     private IGVCommandBar igvCommandBar;
     private javax.swing.JPanel applicationHeaderView;
     private javax.swing.JPanel attributeHeaderPanel;
     private javax.swing.JPanel centerPanel;
     private javax.swing.JPanel cytobandPanel;
-    private javax.swing.JScrollPane dataTrackScrollPane;
-    private javax.swing.JScrollPane featureTrackScrollPane;
+    private TrackPanelScrollPane dataTrackScrollPane;
+    private TrackPanelScrollPane featureTrackScrollPane;
     private javax.swing.JPanel headerPanel;
     private javax.swing.JScrollPane headerScrollPane;
-    private javax.swing.JPanel infoPanel;
-    private javax.swing.JPanel regionOfInterestPane;
+    private javax.swing.JPanel nameHeaderPanel;
+    private RegionOfInterestPanel regionOfInterestPane;
     private javax.swing.JPanel rulerPanel;
     private com.jidesoft.status.StatusBar statusBar;
     private JideSplitPane centerSplitPane;
     public String currentSessionFilePath;
-    // TODO -- Should this really be stored in the frame instance?
-    private AttributeCheckList attributeCheckList;
-
 
     // Most recent sessions
-    final private LinkedList<String> recentSessionList =
-            new LinkedList<String>();
-    // Cursors
-    public static Cursor handCursor;
-    public static Cursor fistCursor;
-    public static Cursor zoomInCursor;
-    public static Cursor zoomOutCursor;
-    public static Cursor dragNDropCursor;
+    final private LinkedList<String> recentSessionList = new LinkedList<String>();
     // FileChooser Dialogs
     private FileChooserDialog trackFileChooser;
     private FileChooser snapshotFileChooser;
     private FileChooser genomeImportFileChooser;
+
     // TODO -- move this to the preferences manager
     private boolean showRegionsOfInterestBarsOn = true;
+
     // Current track filter action.
     //TODO -- A lot of state is passed between the embedded filter in this
     // action and the session during save and restore.  Refactor to pass
     // this state in a single object to/from the session.
     FilterTracksMenuAction filterTracksAction;
-    //
-    final static String ROI_BUTTON_TEXT = "Define a region of interest";
+
     // MenuItems that need to be exposed because
     // they need to be accessed by app code
-    private JCheckBoxMenuItem showAttributeMenuItem;
+    private JCheckBoxMenuItem menuItem;
     private boolean isExportingSnapshot = false;
     private boolean areResourceNodesCheckable = false;
-    private Set<String> inaccessibleGenomeIds = new HashSet();
+
+
     private boolean igvInitialized = false;
     RemoveUserDefinedGenomeMenuAction removeImportedGenomeAction;
+
     // Glass panes
     Component glassPane;
     GhostGlassPane dNdGlassPane;
+
     // Tracksets
-    private final Map<String, TrackSetScrollPane> trackSetScrollPanes = new Hashtable();
+    //private final Map<String, TrackPanelScrollPane> trackSetScrollPanes = new Hashtable();
+
+
+    public static IGVMainFrame getInstance() {
+        if (theInstance == null) {
+            if (Globals.isHeadless()) {
+                System.err.println("Attempt to create IGVMainFrame in headless mode.");
+            } else {
+                theInstance = new IGVMainFrame();
+            }
+        }
+        return theInstance;
+    }
+
+    public static boolean hasInstance() {
+        return theInstance != null;
+    }
+
 
     /**
      * Creates new form IGVMainFrame
      */
     private IGVMainFrame() {
 
+        session = new Session(null);
+        trackManager = new TrackManager(this);
+
         // Create cursors
         createHandCursor();
         createZoomCursors();
@@ -231,32 +245,32 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
         initializeDefaultUserDirectory();
 
-        setTitle(IGVConstants.APPLICATION_NAME);
+        setTitle(UIConstants.APPLICATION_NAME);
 
         initComponents();
 
+        // TODO -- get these from user preferences
+        ToolTipManager.sharedInstance().setDismissDelay(Integer.MAX_VALUE);
+        //ToolTipManager.sharedInstance().setReshowDelay(your time in ms);
+        //ToolTipManager.sharedInstance().setInitialDelay(your time in ms);
+
         // Add the 2 default panels
-        trackSetScrollPanes.put(DATA_PANEL_NAME, (TrackSetScrollPane) dataTrackScrollPane);
+
+        trackManager.putScrollPane(TrackManager.DATA_PANEL_NAME, dataTrackScrollPane);
         if (!PreferenceManager.getInstance().isShowSingleTrackPane()) {
-            trackSetScrollPanes.put(FEATURE_PANEL_NAME, (TrackSetScrollPane) featureTrackScrollPane);
+            trackManager.putScrollPane(TrackManager.FEATURE_PANEL_NAME, featureTrackScrollPane);
         }
 
-        initializeDisplayableAttributes(null);
+        //initializeDisplayableAttributes(null);
 
         // TODO -- figure out why the view context needs this reference
-        DataPanel dp = ((TrackSetScrollPane) dataTrackScrollPane).getDataPanel();
-        model.getViewContext().setDataPanel(dp);
+        DataPanel dp = dataTrackScrollPane.getDataPanel();
+        session.getViewContext().setDataPanel(dp);
 
         pack();
 
         // Setup the content pane widgets
         configureContentPane();
-
-        // Set delay properties for tooltips.
-        ToolTipManager ttm = ToolTipManager.sharedInstance();
-        //ttm.setInitialDelay(1000);
-        ttm.setDismissDelay(Integer.MAX_VALUE);
-
         initializeSnapshot();
         initializeDialogs();
 
@@ -268,7 +282,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
         // Macs are double buffered natively.  Double buffering in java is redundant
         // and has a noticeable effect on performance on Macs.
         // If os is Mac turn double buffering off.
-        if (IGVConstants.IS_MAC) {
+        if (UIConstants.IS_MAC) {
             System.setProperty("apple.awt.graphics.UseQuartz", "true");
             System.setProperty("apple.awt.rendering", "speed");
             // NOTE:  This doesn't seem to have any effect on Leopard.  Retest on Tiger?
@@ -284,30 +298,8 @@ public class IGVMainFrame extends javax.swing.JFrame {
             }
         });
 
-        for (TrackSetScrollPane sp : trackSetScrollPanes.values()) {
-            AttributeManager.getInstance().addPropertyChangeListener(sp.getAttributePanel());
-        }
-
         packViews();
 
-        addComponentListener(
-                new ComponentAdapter() {
-
-                    @Override
-                    public void componentResized(ComponentEvent e) {
-
-                        // Single track pane view if true
-                        boolean isShowSingleTrackPane =
-                                PreferenceManager.getInstance().getShowSingleTrackPane();
-
-                        if (isShowSingleTrackPane) {
-                            //centerSplitPane.setDividerLocation(1.0d);
-                        } else {
-                            //centerSplitPane.setDividerLocation(0.85d);
-                        }
-                    }
-                });
-
         addWindowListener(new WindowAdapter() {
 
             @Override
@@ -327,139 +319,25 @@ public class IGVMainFrame extends javax.swing.JFrame {
     }
 
 
-    public static IGVMainFrame getInstance() {
-        if (theInstance == null) {
-            theInstance = new IGVMainFrame();
-        }
-
-        return theInstance;
-    }
-
-    public static boolean hasInstance() {
-        return theInstance != null;
-    }
-
-    private boolean displayFailedToInitializeGenomeDialog() {
-
-        boolean success = true;
-
-        try {
-            final String[] buttons = {"Select", "Exit"};
-
-            int option =
-                    JOptionPane.showOptionDialog(this, "Failed to initialize the default genome." +
-                            "\nPlease select another genome or exit.",
-                            "Genome Initialization Failure",
-                            JOptionPane.ERROR_MESSAGE,
-                            0,
-                            null,
-                            buttons,
-                            buttons[2]);
-
-            if (option == 0) {
-                GenomeListItem selection = doImportGenomeFromCustomList(); // Select from narrowed genome list
-                if (selection == null) {
-                    return false;
-                }
-
-            } else {
-                System.exit(-1);
-            }
-
-        } catch (Exception e) {
-            log.error("Failure while initializing genome", e);
-            success =
-                    false;
-        }
-
-        return success;
-    }
-
-    public Set<String> getInaccessibleGenomeIds() {
-        return inaccessibleGenomeIds;
-    }
-
-    public void addInaccessibleGenomeId(String id) {
-        this.inaccessibleGenomeIds.add(id);
-    }
-
-    public void removeInaccessibleGenomeId(String id) {
-        this.inaccessibleGenomeIds.remove(id);
-    }
-
     /**
-     * This method is only called by IGV initialization code. It's sole purpose
-     * is to provide the user with the ability to select a genome to load
-     * (from a narrowed list) because IGV failed to load the default genome. The
-     * selection list is narrowed by removing the genome IGV failed to load.
+     * Convenience method
      *
      * @return
      */
-    private GenomeListItem doImportGenomeFromCustomList() {
-
-        Vector<Object> genomes = new Vector();
-
-        // Build a single available genome list from both client, server
-        // and cached information. This allows us to process
-        // everything the same way.
-        LinkedHashSet<GenomeListItem> serverSideItemList = null;
-        try {
-            serverSideItemList = GenomeManager.getInstance().getServerGenomeArchiveList(null);
-        } catch (Exception e) {
-            log.error("Error getting genome archive list", e);
-        }
-
-        LinkedHashSet<GenomeListItem> userDefinedItemList = null;
-        try {
-            userDefinedItemList = GenomeManager.getInstance().getUserDefinedGenomeArchiveList(null);
-        } catch (Exception e) {
-            log.error("Error getting user genome archive list", e);
-        }
-
-        if (userDefinedItemList != null && !userDefinedItemList.isEmpty()) {
-            boolean foundValidIds = false;
-            for (GenomeListItem item : userDefinedItemList) {
-                if (inaccessibleGenomeIds.contains(item.getId())) {
-                    continue; // skip it
-                }
-
-                genomes.add(item);
-                foundValidIds =
-                        true;
-            }
-// If true we have atleast one valid genome
-// id so we need the separator
-
-            if (foundValidIds) {
-                genomes.add(IGVConstants.GENOME_LIST_SEPARATOR);
-            }
-
-        }
-
-        if (serverSideItemList != null && !serverSideItemList.isEmpty()) {
-            for (GenomeListItem item : serverSideItemList) {
-                if (inaccessibleGenomeIds.contains(item.getId())) {
-                    continue; // skip it
-                }
-
-                genomes.add(item);
-            }
-
-        }
+    public ViewContext getViewContext() {
+        return session.getViewContext();
+    }
 
-        JComboBox genomeComboBox = new JComboBox(genomes);
-        genomeComboBox.setRenderer(new IGVCommandBar.ComboBoxRenderer());
 
-        int option = JOptionPane.showConfirmDialog(this, genomeComboBox,
-                "Select Genome", JOptionPane.OK_CANCEL_OPTION);
+    public RegionOfInterest getSelectedRegion() {
+        return regionOfInterestPane.getSelectedRegion();
+    }
 
-        if (option != JOptionPane.OK_OPTION) {
-            return null;
+    public void setSelectedRegion(RegionOfInterest region) {
+        if (region != regionOfInterestPane.getSelectedRegion()) {
+            regionOfInterestPane.setSelectedRegion(region);
+            repaintDataPanels();
         }
-
-        GenomeListItem genomeListItem = (GenomeListItem) genomeComboBox.getSelectedItem();
-        selectGenomeFromList(genomeListItem.getId());
-        return genomeListItem;
     }
 
     public void setCheckingSelectedResourceNodesAllowed(boolean value) {
@@ -478,7 +356,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
         } catch (Exception e) {
             e.printStackTrace();
             String message = "Failed to clear all preferences!";
-            showAndLogErrorMessage(IGVMainFrame.theInstance, message, log, e);
+            MessageUtils.showAndLogErrorMessage(IGVMainFrame.theInstance, message, log, e);
         }
 
     }
@@ -500,13 +378,13 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
     @Override
     public Dimension getPreferredSize() {
-        return IGVConstants.preferredSize;
+        return UIConstants.preferredSize;
     }
 
     private void initializeDefaultUserDirectory() {
 
         // Create the user directory
-        File defaultUserDirectory = new File(DEFAULT_IGV_DIRECTORY);
+        File defaultUserDirectory = getIgvDirectory();
         if (!defaultUserDirectory.exists()) {
             boolean exists = defaultUserDirectory.exists();
             if (!exists) {
@@ -552,8 +430,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
         // File Filters
         FileFilter[] fileFilters = GenericUtilities.getAllSnapshotFileFilters();
 
-        snapshotFileChooser =
-                getFileChooser(snapshotDirectory, null, fileFilters);
+        snapshotFileChooser = getFileChooser(snapshotDirectory, null, fileFilters);
         snapshotFileChooser.setDialogTitle("Snapshot File");
 
         snapshotFileChooser.addPropertyChangeListener(
@@ -588,7 +465,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
                                     final File selectedFile = Utilities.changeFileExtension(
                                             file, newFilter.getExtension());
 
-                                    GuiUtilities.invokeOnEventThread(new Runnable() {
+                                    UIUtilities.invokeOnEventThread(new Runnable() {
 
                                         public void run() {
                                             snapshotFileChooser.setSelectedFile(selectedFile);
@@ -603,31 +480,14 @@ public class IGVMainFrame extends javax.swing.JFrame {
                 });
     }
 
-    /**
-     * Clear the image cache
-     */
-    public void clearImageCacheForTrackPanels() {
-        clearImageCacheWithNoRepaint();
-        repaint();
-
-    }
-
-    public void clearImageCacheWithNoRepaint() {
-        for (TrackSetScrollPane tsv : trackSetScrollPanes.values()) {
-            tsv.getDataPanel().clearImageCache();
-        }
-
-    }
-
     public void addRegionOfInterest(RegionOfInterest roi) {
-        model.addRegionOfInterestWithNoListeners(roi);
-        roi.setParent(regionOfInterestPane);
-        regionOfInterestPane.addMouseListener(roi);
-        regionOfInterestPane.addMouseMotionListener(roi);
+        session.addRegionOfInterestWithNoListeners(roi);
+        regionOfInterestPane.setSelectedRegion(roi);
+        regionOfInterestPane.repaint();
     }
 
     void beginROI(JButton button) {
-        for (TrackSetScrollPane tsv : trackSetScrollPanes.values()) {
+        for (TrackPanelScrollPane tsv : trackManager.getTrackPanelScrollPanes()) {
             DataPanel dp = tsv.getDataPanel();
             RegionOfInterestTool regionOfInterestTool =
                     new RegionOfInterestTool(dp, button);
@@ -637,7 +497,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
     }
 
     void endROI() {
-        for (TrackSetScrollPane tsv : trackSetScrollPanes.values()) {
+        for (TrackPanelScrollPane tsv : trackManager.getTrackPanelScrollPanes()) {
             DataPanel dp = tsv.getDataPanel();
             dp.setCurrentTool(null);
         }
@@ -645,11 +505,13 @@ public class IGVMainFrame extends javax.swing.JFrame {
     }
 
     // TODO -- move this to preferences manager class
+
     public void setShowRegionsOfInterestBarsOn(boolean enabled) {
         showRegionsOfInterestBarsOn = enabled;
     }
 
     // TODO -- move this to preferences manager class
+
     public boolean isShowRegionsOfInterestBarsOn() {
         return showRegionsOfInterestBarsOn;
     }
@@ -660,7 +522,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
     public void chromosomeChangeEvent(boolean updateCommandBar) {
         igvCommandBar.chromosomeChanged();
-        if (model.getViewContext().getChrName().equals(IGVConstants.CHR_ALL)) {
+        if (getViewContext().getChrName().equals(Globals.CHR_ALL)) {
             cytobandPanel.setCursor(Cursor.getDefaultCursor());
             cytobandPanel.setToolTipText("");
         } else {
@@ -669,7 +531,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
             cytobandPanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
         }
 
-        ((InfoPanel) infoPanel).updateHelpText();
+        ((NameHeaderPanel) nameHeaderPanel).updateHelpText();
         repaintDataAndHeaderPanels(updateCommandBar);
 
     }
@@ -691,21 +553,21 @@ public class IGVMainFrame extends javax.swing.JFrame {
     }
 
     public void repaintDataPanels() {
-        for (TrackSetScrollPane tsv : trackSetScrollPanes.values()) {
+        for (TrackPanelScrollPane tsv : trackManager.getTrackPanelScrollPanes()) {
             tsv.getDataPanel().repaint();
         }
 
     }
 
     public void repaintNamePanels() {
-        for (TrackSetScrollPane tsv : trackSetScrollPanes.values()) {
+        for (TrackPanelScrollPane tsv : trackManager.getTrackPanelScrollPanes()) {
             tsv.getNamePanel().repaint();
         }
 
     }
 
     public void repaintStatusAndZoomSlider() {
-        //   igvCommandBar.repaint();
+        igvCommandBar.repaint();
         //   statusBar.repaint();
     }
 
@@ -732,7 +594,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
                     @Override
                     public void componentResized(ComponentEvent e) {
 
-                        GuiUtilities.invokeOnEventThread(new Runnable() {
+                        UIUtilities.invokeOnEventThread(new Runnable() {
 
                             public void run() {
                                 doRefresh();
@@ -750,36 +612,6 @@ public class IGVMainFrame extends javax.swing.JFrame {
             centerSplitPane.add(featureTrackScrollPane, JSplitPane.BOTTOM);
         }
 
-        centerSplitPane.addPropertyChangeListener(
-                new PropertyChangeListener() {
-
-                    public void propertyChange(final PropertyChangeEvent e) {
-                        for (TrackSetScrollPane tsv : getTrackSetScrollPanes()) {
-                            tsv.getDataPanel().doResize();
-                        }
-
-                    }
-                });
-
-        // If data viewport size changes we need to resize to track set pane
-
-        for (TrackSetScrollPane sp : trackSetScrollPanes.values()) {
-            final DataPanel dataPanel = sp.getDataPanel();
-            sp.getViewport().addChangeListener(
-                    new ChangeListener() {
-
-                        public void stateChanged(final ChangeEvent e) {
-
-                            GuiUtilities.invokeOnEventThread(new Runnable() {
-
-                                public void run() {
-                                    dataPanel.doResize();
-                                }
-                            });
-                        }
-                    });
-        }
-
         centerPanel.add(headerScrollPane, BorderLayout.NORTH);
         centerPanel.add(centerSplitPane, BorderLayout.CENTER);
     }
@@ -905,14 +737,12 @@ public class IGVMainFrame extends javax.swing.JFrame {
                 File importDirectory =
                         PreferenceManager.getInstance().getLastGenomeImportDirectory();
                 if (importDirectory == null) {
-                    PreferenceManager.getInstance().setLastGenomeImportDirectory(
-                            new File(IGVConstants.getDefaultUserDirectory()));
+                    PreferenceManager.getInstance().setLastGenomeImportDirectory(UIConstants.getUserDirectory());
                 }
 
                 FileFilter[] fileFilters = {new GenericUtilities.GenomeArchiveFileFilter()};
 
-                genomeImportFileChooser =
-                        getFileChooser(importDirectory, null, fileFilters);
+                genomeImportFileChooser = getFileChooser(importDirectory, null, fileFilters);
                 genomeImportFileChooser.setDialogTitle("Load Genome");
                 genomeImportFileChooser.addPropertyChangeListener(
                         new PropertyChangeListener() {
@@ -947,7 +777,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
                                             final File selectedFile = Utilities.changeFileExtension(
                                                     file, newFilter.getExtension());
 
-                                            GuiUtilities.invokeOnEventThread(new Runnable() {
+                                            UIUtilities.invokeOnEventThread(new Runnable() {
 
                                                 public void run() {
                                                     genomeImportFileChooser.setSelectedFile(
@@ -975,8 +805,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
                     File directory = genomeImportFileChooser.getCurrentDirectory();
                     if (directory != null) {
-                        PreferenceManager.getInstance().setLastGenomeImportDirectory(
-                                directory);
+                        PreferenceManager.getInstance().setLastGenomeImportDirectory(directory);
                     }
 
                     try {
@@ -986,8 +815,11 @@ public class IGVMainFrame extends javax.swing.JFrame {
                         }
 
                         // Import the genome
-                        genomeListItem =
-                                GenomeManager.getInstance().loadGenome(file.getAbsolutePath(), true, monitor);
+
+                        if (log.isDebugEnabled()) {
+                            log.debug("Call loadGenome");
+                        }
+                        genomeListItem = GenomeManager.getInstance().loadGenome(file.getAbsolutePath(), true, monitor);
 
                         igvCommandBar.addToUserDefinedGenomeItemList(genomeListItem);
                         igvCommandBar.selectGenomeFromListWithNoImport(genomeListItem.getId());
@@ -1032,22 +864,17 @@ public class IGVMainFrame extends javax.swing.JFrame {
         return genomeListItem;
     }
 
-    public IGVModel getModel() {
-        return model;
-    }
-
     private List<AbstractButton> createMenus() {
 
         List<AbstractButton> menus = new ArrayList<AbstractButton>();
         menus.add(createFileMenu());
         menus.add(createViewMenu());
         menus.add(createTracksMenu());
+        //menus.add(createToolsMenu());
 
-        toolMenu =
-                createToolMenu();
-        toolMenu.setVisible(false);
-        menus.add(toolMenu);
-        menus.add(createToolMenu());
+        extrasMenu = createExtrasMenu();
+        extrasMenu.setVisible(false);
+        menus.add(extrasMenu);
 
         menus.add(createHelpMenu());
 
@@ -1056,8 +883,8 @@ public class IGVMainFrame extends javax.swing.JFrame {
         return menus;
     }
 
-    public void enableToolMenu() {
-        toolMenu.setVisible(true);
+    public void enableExtrasMenu() {
+        extrasMenu.setVisible(true);
     }
 
     /**
@@ -1066,9 +893,12 @@ public class IGVMainFrame extends javax.swing.JFrame {
      * @param locators
      */
     public void loadTracks(final Collection<ResourceLocator> locators) {
-        loadTracks(locators, true);
-
+        loadTracks(locators, false);
+    }
 
+    public void unloadTracks(final Collection<ResourceLocator> locators) {
+        trackManager.unloadTracks(locators);
+        this.doRefresh();
     }
 
     /**
@@ -1081,97 +911,76 @@ public class IGVMainFrame extends javax.swing.JFrame {
     public void loadTracks(final Collection<ResourceLocator> locators, boolean doInBackground) {
 
         ((ApplicationStatusBar) statusBar).setMessage("Loading ...");
-        Runnable loader = new Runnable() {
 
-            public void run() {
-
-                log.debug("Run loadTracks");
-
-                boolean tracksWereLoaded = false;
+        log.debug("Run loadTracks");
 
-                try {
-                    if (locators != null && !locators.isEmpty()) {
-
-                        // get current track count per panel.  Needed to detect which panels
-                        // changed.  Also record panel sizes
-                        final HashMap<TrackSetScrollPane, Integer> trackCountMap = new HashMap();
-                        final HashMap<TrackSetScrollPane, Integer> panelSizeMap = new HashMap();
-                        for (TrackSetScrollPane sp : trackSetScrollPanes.values()) {
-                            trackCountMap.put(sp, sp.getDataPanel().getAllTracks().size());
-                            panelSizeMap.put(sp, sp.getDataPanel().getHeight());
-                        }
-
-                        tracksWereLoaded = TrackManager.getInstance().loadResources(locators);
-
-                        if (tracksWereLoaded) {
-                            GuiUtilities.invokeOnEventThread(new Runnable() {
-
-                                public void run() {
-                                    clearImageCacheWithNoRepaint();
-
-                                    for (TrackSetScrollPane sp : trackSetScrollPanes.values()) {
-                                        if (trackCountMap.containsKey(sp)) {
-                                            int prevTrackCount = trackCountMap.get(sp).intValue();
-                                            if (prevTrackCount != sp.getDataPanel().getAllTracks().size()) {
-                                                sp.getDataPanel().doResize();
-                                                int scrollPosition = panelSizeMap.get(sp);
-                                                if (prevTrackCount != 0 && sp.getVerticalScrollBar().isShowing()) {
-                                                    sp.getVerticalScrollBar().setMaximum(
-                                                            sp.getDataPanel().getHeight());
-                                                    sp.getVerticalScrollBar().setValue(
-                                                            scrollPosition);
-                                                }
+        boolean tracksWereLoaded = false;
+        CursorToken token = null;
 
-                                            }
-                                        }
-                                    }
+        try {
+            token = WaitCursorManager.showWaitCursor();
+            if (locators != null && !locators.isEmpty()) {
+
+                // get current track count per panel.  Needed to detect which panels
+                // changed.  Also record panel sizes
+                final HashMap<TrackPanelScrollPane, Integer> trackCountMap = new HashMap();
+                final HashMap<TrackPanelScrollPane, Integer> panelSizeMap = new HashMap();
+                for (TrackPanelScrollPane sp : trackManager.getTrackPanelScrollPanes()) {
+                    trackCountMap.put(sp, sp.getDataPanel().getAllTracks().size());
+                    panelSizeMap.put(sp, sp.getDataPanel().getHeight());
+                }
 
-                                    // Adjust divider for data panel.  The data panel divider can be
-                                    // zero if there are no data tracks loaded.
-                                    TrackSetScrollPane dsp = trackSetScrollPanes.get(IGVMainFrame.DATA_PANEL_NAME);
-                                    if (dsp.getDataPanel().getAllTracks().size() > 0 &&
-                                            centerSplitPane.getDividerLocation(0) < 10) {
-                                        centerSplitPane.setDividerLocation(0, 40);
-                                    }
+                tracksWereLoaded = getTrackManager().loadResources(locators);
 
-                                    doRefresh();
+                if (tracksWereLoaded) {
 
+                    for (TrackPanelScrollPane sp : trackManager.getTrackPanelScrollPanes()) {
+                        if (trackCountMap.containsKey(sp)) {
+                            int prevTrackCount = trackCountMap.get(sp).intValue();
+                            if (prevTrackCount != sp.getDataPanel().getAllTracks().size()) {
+                                //TODO Do not do a resize if this is a session reload
+                                sp.getDataPanel().doResize();
+                                int scrollPosition = panelSizeMap.get(sp);
+                                if (prevTrackCount != 0 && sp.getVerticalScrollBar().isShowing()) {
+                                    sp.getVerticalScrollBar().setMaximum(sp.getDataPanel().getHeight());
+                                    sp.getVerticalScrollBar().setValue(scrollPosition);
                                 }
-                            });
+                            }
                         }
                     }
-                } catch (Exception e) {
-                    if (!(e instanceof ConcurrentModificationException)) {
-                        if (e.getMessage() != null && e.getMessage().length() > 8) {
-                            UIUtilities.showAndLogErrorMessage(null, e.getMessage(), log);
-                        } else {
-                            log.error(e);
-                            UIUtilities.showErrorMessage(null, "An error occurred while loading tracks. " +
-                                    "Please check the logs for details.");
-                        }
+
+                    // Adjust divider for data panel.  The data panel divider can be
+                    // zero if there are no data tracks loaded.
+                    TrackPanelScrollPane dsp = trackManager.getScrollPane(TrackManager.DATA_PANEL_NAME);
+                    if (dsp.getDataPanel().getAllTracks().size() > 0 &&
+                            centerSplitPane.getDividerLocation(0) < 10) {
+                        centerSplitPane.setDividerLocation(0, 40);
                     }
-                } finally {
-                    showLoadedTrackCount();
-                }
-                log.debug("Finish loadTracks");
 
-            }
-        };
+                    doRefresh();
 
-        if (doInBackground) {
-            LongRunningTask.submit(loader);
-        } else {
-            loader.run();
+                }
+            }
+        } catch (Exception e) {
+            if (!(e instanceof ConcurrentModificationException)) {
+                if (e.getMessage() != null && e.getMessage().length() > 8) {
+                    MessageUtils.showMessage(e.getMessage());
+                } else {
+                    log.error(e);
+                    MessageUtils.showMessage("An error occurred while loading tracks. " +
+                            "Please check the logs for details.");
+                }
+            }
+        } finally {
+            showLoadedTrackCount();
+            if (token != null) {
+                WaitCursorManager.removeWaitCursor(token);
+            }
         }
+        log.debug("Finish loadTracks");
 
     }
 
-    public void updateAttributePanel() {
-        // Need this because TrackManager loads some attributes
-        // internally. So we need to pick them up.
-        initializeDisplayableAttributes(AttributeManager.getInstance().getAttributeKeys());
-    }
-
     private JMenu createFileMenu() {
 
         List<JComponent> menuItems = new ArrayList<JComponent>();
@@ -1183,14 +992,14 @@ public class IGVMainFrame extends javax.swing.JFrame {
         menuAction = new LoadFilesMenuAction("Load from File...", KeyEvent.VK_L, this);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
-        menuAction = new LoadFromURLMenuAction("Load from URL...", KeyEvent.VK_U, this);
+        menuAction = new LoadFromURLMenuAction(LoadFromURLMenuAction.LOAD_FROM_URL, KeyEvent.VK_U, this);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
         menuAction = new LoadFromServerAction("Load from Server...", KeyEvent.VK_S, this);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
-        //menuAction = new LoadFromURLMenuAction("Load from DAS Server...", KeyEvent.VK_D, this);
-        //menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
+        menuAction = new LoadFromURLMenuAction(LoadFromURLMenuAction.LOAD_FROM_DAS, KeyEvent.VK_D, this);
+        menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
         menuItems.add(new JSeparator());
 
@@ -1207,7 +1016,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
         menuItems.add(new JSeparator());
 
         menuAction =
-                new MenuAction(IGVConstants.IMPORT_GENOME_LIST_MENU_ITEM, null, KeyEvent.VK_D) {
+                new MenuAction(UIConstants.IMPORT_GENOME_LIST_MENU_ITEM, null, KeyEvent.VK_D) {
 
                     @Override
                     public void actionPerformed(ActionEvent event) {
@@ -1229,18 +1038,18 @@ public class IGVMainFrame extends javax.swing.JFrame {
                     }
                 };
 
-        menuAction.setToolTipText(UIConstants.IMPORT_GENOME_TOOLTIP);
+        menuAction.setToolTipText(IMPORT_GENOME_TOOLTIP);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
         boolean hasImportedGenomes = true;
         try {
-            hasImportedGenomes = !GenomeManager.getInstance().getUserDefinedGenomeArchiveList(null).isEmpty();
+            hasImportedGenomes = !GenomeManager.getInstance().getUserDefinedGenomeArchiveList().isEmpty();
 
         } catch (IOException iOException) {
             // Ignore
         }
         removeImportedGenomeAction = new RemoveUserDefinedGenomeMenuAction(
-                IGVConstants.REMOVE_GENOME_LIST_MENU_ITEM, KeyEvent.VK_R);
+                UIConstants.REMOVE_GENOME_LIST_MENU_ITEM, KeyEvent.VK_R);
         removeImportedGenomeAction.setEnabled(hasImportedGenomes);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(removeImportedGenomeAction));
 
@@ -1256,29 +1065,29 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
                     @Override
                     public void actionPerformed(ActionEvent e) {
-                        doApplicationSnapshot();
+                        doApplicationSnapshot(centerPanel);
 
                     }
                 };
 
-        menuAction.setToolTipText(UIConstants.SAVE_IMAGE_TOOLTIP);
+        menuAction.setToolTipText(SAVE_IMAGE_TOOLTIP);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
         menuItems.add(new JSeparator());
 
         // Export Regions
-        menuAction = new ExportRegionsMenuAction("Export Regions ...", KeyEvent.VK_E, this);
+        menuAction = new ExportRegionsMenuAction("Export Regions...", KeyEvent.VK_E, this);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
 
         // Import Regions
-        menuAction = new ImportRegionsMenuAction("Import Regions ...", KeyEvent.VK_I, this);
-        menuAction.setToolTipText(UIConstants.IMPORT_REGION_TOOLTIP);
+        menuAction = new ImportRegionsMenuAction("Import Regions...", KeyEvent.VK_I, this);
+        menuAction.setToolTipText(IMPORT_REGION_TOOLTIP);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
         // Import Regions
-        menuAction = new ClearRegionsMenuAction("Clear Regions ...", this);
-        menuAction.setToolTipText(UIConstants.IMPORT_REGION_TOOLTIP);
+        menuAction = new ClearRegionsMenuAction("Clear Regions...", this);
+        menuAction.setToolTipText(IMPORT_REGION_TOOLTIP);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
         // Separator
@@ -1298,6 +1107,21 @@ public class IGVMainFrame extends javax.swing.JFrame {
         // Separator
         menuItems.add(new JSeparator());
 
+        menuAction = new LoadFromScriptMenuAction("Run Batch Script...", KeyEvent.VK_X, this);
+        menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
+
+
+        menuItems.add(new JSeparator());
+
+        menuAction = new SortTracksMenuAction("Run igvtools...", KeyEvent.VK_T, this) {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                IgvToolsGui.launch(false, session.getViewContext().getGenomeId());
+            }
+        };
+        menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
+
+        menuItems.add(new JSeparator());
         // Exit
         menuAction =
                 new MenuAction("Exit", null, KeyEvent.VK_X) {
@@ -1308,7 +1132,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
                     }
                 };
 
-        menuAction.setToolTipText(UIConstants.EXIT_TOOLTIP);
+        menuAction.setToolTipText(EXIT_TOOLTIP);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
 
@@ -1334,8 +1158,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
             // Now add menu items
             for (final String session : recentSessionList) {
-                OpenSessionMenuAction osMenuAction = new OpenSessionMenuAction(session, new File(
-                        session), this);
+                OpenSessionMenuAction osMenuAction = new OpenSessionMenuAction(session, new File(session), this);
                 menuItems.add(MenuAndToolbarUtils.createMenuItem(osMenuAction));
             }
 
@@ -1351,30 +1174,25 @@ public class IGVMainFrame extends javax.swing.JFrame {
         MenuAction menuAction = null;
 
         // Sort Context
-        menuAction =
-                new SortTracksMenuAction("Sort Tracks ...", KeyEvent.VK_S, this);
+        menuAction = new SortTracksMenuAction("Sort Tracks ...", KeyEvent.VK_S, this);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
-        menuAction =
-                new GroupTracksMenuAction("Group Tracks  ... ", KeyEvent.VK_G, this);
+        menuAction = new GroupTracksMenuAction("Group Tracks  ... ", KeyEvent.VK_G, this);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
         // Filter Tracks
-        filterTracksAction =
-                new FilterTracksMenuAction("Filter Tracks ...", KeyEvent.VK_F, this);
+        filterTracksAction = new FilterTracksMenuAction("Filter Tracks ...", KeyEvent.VK_F, this);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(filterTracksAction));
 
         menuItems.add(new JSeparator());
 
         // Reset Tracks
-        menuAction =
-                new FitDataToWindowMenuAction("Fit Data to Window", KeyEvent.VK_W, this);
+        menuAction = new FitDataToWindowMenuAction("Fit Data to Window", KeyEvent.VK_W, this);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
 
         // Set track height
-        menuAction =
-                new SetTrackHeightMenuAction("Set Track Height...", KeyEvent.VK_H, this);
+        menuAction = new SetTrackHeightMenuAction("Set Track Height...", KeyEvent.VK_H, this);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
 
@@ -1394,7 +1212,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
                     @Override
                     public void actionPerformed(ActionEvent e) {
 
-                        GuiUtilities.invokeOnEventThread(new Runnable() {
+                        UIUtilities.invokeOnEventThread(new Runnable() {
 
                             public void run() {
                                 doViewPreferences();
@@ -1402,7 +1220,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
                         });
                     }
                 };
-        menuAction.setToolTipText(UIConstants.PREFERENCE_TOOLTIP);
+        menuAction.setToolTipText(PREFERENCE_TOOLTIP);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
         menuAction =
@@ -1413,13 +1231,14 @@ public class IGVMainFrame extends javax.swing.JFrame {
                         (new LegendDialog(IGVMainFrame.this, false)).setVisible(true);
                     }
                 };
-        menuAction.setToolTipText(UIConstants.SHOW_HEATMAP_LEGEND_TOOLTIP);
+        menuAction.setToolTipText(SHOW_HEATMAP_LEGEND_TOOLTIP);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
         menuItems.add(new JSeparator());
 
         // Hide or Show the attribute panels
         boolean isShow = PreferenceManager.getInstance().getShowAttributeView();
+        doShowAttributeDisplay(isShow);  // <= WEIRD doing this here!
 
         menuAction =
                 new MenuAction("Show Attribute Display", null, KeyEvent.VK_A) {
@@ -1427,16 +1246,14 @@ public class IGVMainFrame extends javax.swing.JFrame {
                     @Override
                     public void actionPerformed(ActionEvent e) {
 
-                        JCheckBoxMenuItem menuItem =
-                                (JCheckBoxMenuItem) e.getSource();
+                        JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem) e.getSource();
                         doShowAttributeDisplay(menuItem.getState());
                     }
                 };
-        menuAction.setToolTipText(UIConstants.SHOW_ATTRIBUTE_DISPLAY_TOOLTIP);
-        showAttributeMenuItem =
-                MenuAndToolbarUtils.createMenuItem(menuAction, isShow);
-        menuItems.add(showAttributeMenuItem);
-        doShowAttributeDisplay(isShow);
+        menuAction.setToolTipText(SHOW_ATTRIBUTE_DISPLAY_TOOLTIP);
+        menuItem = MenuAndToolbarUtils.createMenuItem(menuAction, isShow);
+        menuItems.add(menuItem);
+
 
         menuAction =
                 new MenuAction("Select Attributes to Show...", null, KeyEvent.VK_S) {
@@ -1446,27 +1263,24 @@ public class IGVMainFrame extends javax.swing.JFrame {
                         doSelectDisplayableAttribute();
                     }
                 };
-        menuAction.setToolTipText(UIConstants.SELECT_DISPLAYABLE_ATTRIBUTES_TOOLTIP);
+        menuAction.setToolTipText(SELECT_DISPLAYABLE_ATTRIBUTES_TOOLTIP);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
-
-        // Hide or Show the Regions of Interest
-        /*boolean isShowROI = PreferenceManager.getInstance().getShowRegionTool();
+        menuItems.add(new JSeparator());
         menuAction =
-        new MenuAction("Enable Regions of Interest", null, KeyEvent.VK_E) {
-        @Override
-        public void actionPerformed(ActionEvent e) {
-        JCheckBoxMenuItem menuItem =
-        (JCheckBoxMenuItem) e.getSource();
-        doEnableRegionsOfInterest(menuItem.getState());
-        }
-        };
-        menuAction.setToolTipText(UIConstants.ENABLE_REGIONS_OF_INTEREST_TOOLTIP);
-        regionsOfInterestMenuItem =
-        MenuAndToolbarUtils.createMenuItem(menuAction, isShowROI);
-        menuItems.add(regionsOfInterestMenuItem);
-        doEnableRegionsOfInterest(isShowROI);
-         * */
+                new MenuAction("Show Region Bars", null, KeyEvent.VK_A) {
+
+                    @Override
+                    public void actionPerformed(ActionEvent e) {
+
+                        JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem) e.getSource();
+                        PreferenceManager.getInstance().setShowRegionBars(menuItem.isSelected());
+                        repaintDataPanels();
+                    }
+                };
+        //menuAction.setToolTipText(SHOW_ATTRIBUTE_DISPLAY_TOOLTIP);
+        menuItem = MenuAndToolbarUtils.createMenuItem(menuAction, PreferenceManager.getInstance().isShowRegionBars());
+        menuItems.add(menuItem);
 
         menuItems.add(new JSeparator());
         menuAction =
@@ -1474,83 +1288,54 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
                     @Override
                     public void actionPerformed(ActionEvent e) {
-                        doRefreshAndImageCacheClear();
+                        doRefresh();
                     }
                 };
-        menuAction.setToolTipText(UIConstants.REFRESH_TOOLTIP);
+        menuAction.setToolTipText(REFRESH_TOOLTIP);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
 
-        /* SAVE THIS CODE FOR DEBUGGING
-       menuAction =
-       new MenuAction("Use image caching", null, KeyEvent.VK_C) {
-       @Override
-       public void actionPerformed(ActionEvent e) {
-       JCheckBoxMenuItem menuItem =
-       (JCheckBoxMenuItem) e.getSource();
-       boolean isSelected = menuItem.getState();
-       ((DataPanel) dataTrackPanel).setCaching(isSelected);
-       doRefresh();
-       }
-       };
-       boolean linkedSorting = ((DataPanel) dataTrackPanel).linkedSorting();
-       menuAction.setToolTipText(UIConstants.USE_IMAGE_CACHING_TOOLTIP);
-       menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction, linkedSorting));
-        */
+        menuItems.add(new HistoryMenu("Go to"));
+
 
-        // Add to View menu
+        // Add to IGVPanel menu
         MenuAction dataMenuAction = new MenuAction("View", null, KeyEvent.VK_V);
         return MenuAndToolbarUtils.createMenu(menuItems, dataMenuAction);
+
+
     }
 
-    private JMenu createToolMenu() {
+    private JMenu createToolsMenu() {
 
         List<JComponent> menuItems = new ArrayList<JComponent>();
-
         MenuAction menuAction = null;
 
-        menuAction =
-                new MenuAction("Preprocess ...", null, KeyEvent.VK_P) {
-
-                    @Override
-                    public void actionPerformed(ActionEvent e) {
-                        //(new ToolsDialog(IGVMainFrame.this, false)).setVisible(true);
-                    }
-                };
-
-        menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
-
-        menuItems.add(new JSeparator());
-
-        menuAction = new ResetPreferencesAction("Reset Preferences", this);
+        // Sort Context
+        menuAction = new SortTracksMenuAction("Launch Tools...", KeyEvent.VK_T, this) {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                IgvToolsGui.launch(false, session.getViewContext().getGenomeId());
+            }
+        };
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
+        MenuAction toolsMenuAction = new MenuAction("Tools", null, KeyEvent.VK_T);
+        return MenuAndToolbarUtils.createMenu(menuItems, toolsMenuAction);
 
-        //menuAction = new LaunchGeneEAction("Launch Gene-E", this);
-        //menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
-
-        //menuAction = new MacroSnapshotAction("Macro Snapshots ... ", this);
-        //menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
+    }
 
-        // Direct Draw Disabled - Windows Only
-        if (!IS_MAC) {
 
-            // Separator
-            menuItems.add(new JSeparator());
+    private JMenu createExtrasMenu() {
 
-            // Is Direct Draw Disabled
-            PreferenceManager manager = PreferenceManager.getInstance();
-            boolean isDirectDrawDisabled = manager.getDirectDrawDisabled();
-            menuAction =
-                    new DisableDirectDrawAction("Disable Direct Draw", KeyEvent.VK_D, this);
-            menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction, isDirectDrawDisabled));
-            if (isDirectDrawDisabled) {
-                System.setProperty("sun.java2d.noddraw", "true");
-            }
+        List<JComponent> menuItems = new ArrayList<JComponent>();
 
-        }
+        MenuAction menuAction = null;
 
+        // Preferences reset
+        menuAction = new ResetPreferencesAction("Reset Preferences", this);
+        menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
+        // Linked sorting
         menuAction =
                 new MenuAction("Use Linked Sorting", null, KeyEvent.VK_C) {
 
@@ -1566,22 +1351,6 @@ public class IGVMainFrame extends javax.swing.JFrame {
         menuAction.setToolTipText("Enable linked sorting");
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction, linkedSorting));
 
-        // ROC curves
-        menuAction =
-                new MenuAction("Enable ROC", null, KeyEvent.VK_C) {
-
-                    @Override
-                    public void actionPerformed(ActionEvent e) {
-                        JCheckBoxMenuItem menuItem =
-                                (JCheckBoxMenuItem) e.getSource();
-                        boolean isSelected = menuItem.getState();
-                        ROC.ENABLED = isSelected;
-                    }
-                };
-        boolean rocEnabled = ROC.ENABLED;
-        menuAction.setToolTipText("Enable ROC curve for gene searching.");
-        menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction, rocEnabled));
-
 
         menuItems.add(new JSeparator());
 
@@ -1609,28 +1378,42 @@ public class IGVMainFrame extends javax.swing.JFrame {
                     }
                 };
 
-        menuAction.setToolTipText(UIConstants.LOAD_GENOME_TOOLTIP);
+        menuAction.setToolTipText(LOAD_GENOME_TOOLTIP);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
-        // Label y axis charts
-        final JCheckBoxMenuItem cbMenuItem = new JCheckBoxMenuItem("Label Y Axis on Charts");
-        PreferenceManager.ChartPreferences cp = PreferenceManager.getInstance().getChartPreferences();
-        cbMenuItem.setSelected(cp.isDrawAxis());
-        cbMenuItem.addActionListener(new ActionListener() {
+        // Set frame dimensions
+        menuAction =
+                new MenuAction("Set window dimensions", null, KeyEvent.VK_C) {
 
-            public void actionPerformed(ActionEvent arg0) {
-                PreferenceManager.getInstance().put(
-                        PreferenceManager.ChartPreferences.Y_AXIS,
-                        String.valueOf(cbMenuItem.isSelected()));
-                repaint();
+                    @Override
+                    public void actionPerformed(ActionEvent e) {
+                        String value = JOptionPane.showInputDialog("Enter dimensions, e.g. 800x400");
+                        String[] vals = value.split("x");
+                        if (vals.length == 2) {
+                            int w = Integer.parseInt(vals[0]);
+                            int h = Integer.parseInt(vals[1]);
+                            IGVMainFrame.getInstance().setSize(w, h);
+                        }
+                    }
+                };
+        menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
-            }
-        });
-        menuItems.add(cbMenuItem);
+        // Save entire window
+        menuAction =
+                new MenuAction("Save Screenshot ...", null, KeyEvent.VK_A) {
 
+                    @Override
+                    public void actionPerformed(ActionEvent e) {
+                        doApplicationSnapshot(getContentPane());
 
-        MenuAction toolMenuAction = new MenuAction("Tools");
-        JMenu menu = MenuAndToolbarUtils.createMenu(menuItems, toolMenuAction);
+                    }
+                };
+
+        menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
+
+
+        MenuAction extrasMenuAction = new MenuAction("Extras");
+        JMenu menu = MenuAndToolbarUtils.createMenu(menuItems, extrasMenuAction);
         menu.setVisible(false);
 
 
@@ -1656,7 +1439,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
                     }
                 };
-        menuAction.setToolTipText(UIConstants.HELP_TOOLTIP);
+        menuAction.setToolTipText(HELP_TOOLTIP);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
 
@@ -1673,7 +1456,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
                     }
                 };
-        menuAction.setToolTipText(UIConstants.TUTORIAL_TOOLTIP);
+        menuAction.setToolTipText(TUTORIAL_TOOLTIP);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
         menuAction =
@@ -1684,7 +1467,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
                         (new AboutDialog(IGVMainFrame.this, true)).setVisible(true);
                     }
                 };
-        menuAction.setToolTipText(UIConstants.ABOUT_TOOLTIP);
+        menuAction.setToolTipText(ABOUT_TOOLTIP);
         menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
 
         MenuAction toolMenuAction = new MenuAction("Help");
@@ -1698,15 +1481,6 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
     }
 
-    /**
-     * TODO  Remove this method and replace all references to with
-     * a direct call to clearImageCacheWithNoRepaint();
-     *
-     * @deprecated
-     */
-    public void fireViewChangedEvent() {
-        clearImageCacheWithNoRepaint();
-    }
 
     /**
      * Select a genome
@@ -1721,7 +1495,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
             if (genomeType != null) {
 
                 final String genomeId = genomeType.getId();
-                String currentGenomeId = IGVModel.getInstance().getViewContext().getGenomeId();
+                String currentGenomeId = session.getViewContext().getGenomeId();
                 if (currentGenomeId != null && genomeId.equalsIgnoreCase(currentGenomeId)) {
                     // Nothing to do if genome already loaded
                     return;
@@ -1729,7 +1503,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
                 setGenomeId(genomeId);
                 PreferenceManager.getInstance().setDefaultGenome(genomeId);
-                TrackManager.getInstance().reloadSAMTracks();
+                IGVMainFrame.getInstance().getTrackManager().reloadSAMTracks();
             }
 
         } finally {
@@ -1743,7 +1517,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
      */
     final public void doViewPreferences() {
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
 
@@ -1803,8 +1577,8 @@ public class IGVMainFrame extends javax.swing.JFrame {
             if (!recentSessionList.isEmpty()) {
 
                 int size = recentSessionList.size();
-                if (size > IGVConstants.NUMBER_OF_RECENT_SESSIONS_TO_LIST) {
-                    size = IGVConstants.NUMBER_OF_RECENT_SESSIONS_TO_LIST;
+                if (size > UIConstants.NUMBER_OF_RECENT_SESSIONS_TO_LIST) {
+                    size = UIConstants.NUMBER_OF_RECENT_SESSIONS_TO_LIST;
                 }
 
                 String recentSessions = "";
@@ -1834,40 +1608,31 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
     final public void doShowAttributeDisplay(boolean enableAttributeView) {
 
-        boolean oldState =
-                PreferenceManager.getInstance().getShowAttributeView();
+        boolean oldState = PreferenceManager.getInstance().getShowAttributeView();
 
         // First store the newly requested state
         PreferenceManager.getInstance().setShowAttributeView(enableAttributeView);
 
-        showAttributeMenuItem.setSelected(enableAttributeView);
+        //menuItem.setSelected(enableAttributeView);
 
         // Now, if the state has actually change we
         // need to refresh everything
         if (oldState != enableAttributeView) {
-            packViews();
             doRefresh();
-
         }
 
 
     }
 
-    final public void doRefreshAndImageCacheClear() {
-        clearImageCacheForTrackPanels();
-        doRefresh();
-
-    }
 
     final public void doRefresh() {
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
                 try {
                     doResizeTrackPanels();
                     packViews();
-
                     getContentPane().validate();
                     getContentPane().repaint();
                 } finally {
@@ -1878,15 +1643,19 @@ public class IGVMainFrame extends javax.swing.JFrame {
         });
     }
 
+    final public void refreshCommandBar() {
+        igvCommandBar.updateCurrentCoordinates();
+    }
+
     final public void refreshFeatureTrackView() {
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
                 try {
                     packViews();
-                    for (TrackSetScrollPane sp : trackSetScrollPanes.values()) {
-                        sp.getTrackSetView().validate();
+                    for (TrackPanelScrollPane sp : trackManager.getTrackPanelScrollPanes()) {
+                        sp.getTrackPanel().validate();
                     }
 
                 } finally {
@@ -1899,20 +1668,13 @@ public class IGVMainFrame extends javax.swing.JFrame {
     }
 
     final public void doResizeTrackPanels() {
-        clearImageCacheWithNoRepaint();
-        GuiUtilities.invokeOnEventThread(new Runnable() {
 
-            public void run() {
-
-                for (TrackSetScrollPane tsp : trackSetScrollPanes.values()) {
-                    DataPanel dp = tsp.getDataPanel();
-                    dp.doResize();
-                    dp.validate();
-                    dp.repaint();
-                }
-
-            }
-        });
+        for (TrackPanelScrollPane tsp : trackManager.getTrackPanelScrollPanes()) {
+            DataPanel dp = tsp.getDataPanel();
+            dp.doResize();
+            dp.validate();
+            dp.repaint();
+        }
     }
 
 // TODO -- move all of this attribute stuf out of IGVMainFrame,  perhaps to
@@ -1921,118 +1683,26 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
     final public void doSelectDisplayableAttribute() {
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
-
-            public void run() {
-
-                try {
-
-                    ((ApplicationStatusBar) statusBar).setMessage("Select Attributes to Show...");
-
-                    attributeCheckList.update();
-                    attributeCheckList.setEditing(true);
-                    int status =
-                            JOptionPane.showConfirmDialog(
-                                    IGVMainFrame.this,
-                                    attributeCheckList,
-                                    "Select Attributes to Show",
-                                    JOptionPane.OK_CANCEL_OPTION,
-                                    JOptionPane.PLAIN_MESSAGE,
-                                    null);
-
-                    if (status == JOptionPane.CANCEL_OPTION ||
-                            status == JOptionPane.CLOSED_OPTION) {
-
-                        attributeCheckList.cancelChanges();
-                        attributeCheckList.setEditing(false);
-                        return;
-                    }
-
-                    attributeCheckList.setEditing(false);
-                    attributeCheckList.update();
-
-                    // Save the user's choices
-                    AttributeManager.getInstance().
-                            firePropertyChange(
-                                    AttributeManager.getInstance(),
-                                    AttributeManager.ATTRIBUTES_NARROWED_PROPERTY,
-                                    null,
-                                    null);
-
-                } finally {
-
-                    attributeCheckList.setEditing(false);
-
-                    // Refresh view
-                    packViews();
-
-                    doRefresh();
-
-                    resetStatusMessage();
-
-                }
-            }
-        });
-    }
-
-    private void initializeDisplayableAttributes(List<String> attributeNames) {
-
-        // Load requested preferences first
-        if (attributeCheckList == null) {
-            attributeCheckList = new AttributeCheckList(true);
-        }
-
-        if (attributeNames != null) {
-            attributeCheckList.addItems(attributeNames, true);
-        }
-
-        attributeCheckList.sort();
-    }
-
-    public void setHiddenAttributes(Collection<String> hiddenAttributes) {
-
-        attributeCheckList.deselectItems(hiddenAttributes);
-
-    }
-
-    /**
-     * Return the subset of attribute keys that shoould be displayed in the
-     * attribute panel.  By default this returns all keys not beggining with
-     * a # sign, but the user can select a subset.
-     *
-     * @return
-     */
-    public List<String> getDisplayableAttributes() {
-
-        if (attributeCheckList == null) {
-            initializeDisplayableAttributes(null);
-        }
-
-        List<String> attributesToShow = new ArrayList<String>();
-        HashSet<String> selectedAttributes = attributeCheckList.getSelectedAttributes();
-        if (!selectedAttributes.isEmpty()) {
-            List<String> attributeKeys = AttributeManager.getInstance().getAttributeKeys();
-            for (String attribute : attributeKeys) {
-                if (!attribute.startsWith("#") && selectedAttributes.contains(attribute.trim())) {
-                    attributesToShow.add(attribute);
-                }
+        List<String> allAttributes = AttributeManager.getInstance().getAttributeKeys();
+        Set<String> hiddenAttributes = AttributeManager.getInstance().getHiddenAttributes();
+        final CheckListDialog dlg = new CheckListDialog(this, allAttributes, hiddenAttributes, false);
+        dlg.setVisible(true);
 
-            }
+        if (!dlg.isCanceled()) {
+            AttributeManager.getInstance().setHiddenAttributes(dlg.getNonSelections());
+            doRefresh();
+            getContentPane().repaint();
         }
 
-        Collections.sort(attributesToShow, AttributeManager.getInstance().getAttributeComparator());
-        return attributesToShow;
     }
 
-    public Collection<String> getUnselectedAttributes() {
-        return attributeCheckList.getUnselectedAttributes();
-    }
 
-    final public void doApplicationSnapshot() {
+    final public void doApplicationSnapshot(Component target) {
         ((ApplicationStatusBar) statusBar).setMessage("Creating snapshot...");
         File defaultFile = new File("igv_snapshot.png");
         try {
-            createSnapshot(centerPanel, defaultFile);
+            //createSnapshot(this, defaultFile);
+            createSnapshot(target, defaultFile);
         } catch (Exception e) {
             log.error("Error exporting  image ", e);
             MessageUtils.showMessage(("Error encountered while exporting image: " + e.getMessage()));
@@ -2049,43 +1719,43 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
     final public void createSnapshot(final Component target, final File defaultFile) {
 
-        LongRunningTask.submit(new Runnable() {
-            public void run() {
-                try {
-                    ((ApplicationStatusBar) statusBar).setMessage("Exporting image: " + defaultFile.getAbsolutePath());
-                    File file = selectSnapshotFile(defaultFile);
-                    if (file == null) {
-                        return;
-                    }
-                    isExportingSnapshot = true;
-                    createSnapshotNonInteractive(target, file);
-                } catch (Exception e) {
-                    log.error("Error creating exporting image ", e);
-                    MessageUtils.showMessage(("Error creating the image file: " + defaultFile + "<br> "
-                            + e.getMessage()));
-                }
-                finally {
-                    resetStatusMessage();
-                    isExportingSnapshot = false;
-                }
-
+        CursorToken token = WaitCursorManager.showWaitCursor();
+        try {
+            ((ApplicationStatusBar) statusBar).setMessage("Exporting image: " + defaultFile.getAbsolutePath());
+            File file = selectSnapshotFile(defaultFile);
+            if (file == null) {
+                return;
             }
-        });
+            isExportingSnapshot = true;
+            createSnapshotNonInteractive(target, file);
+        } catch (Exception e) {
+            log.error("Error creating exporting image ", e);
+            MessageUtils.showMessage(("Error creating the image file: " + defaultFile + "<br> "
+                    + e.getMessage()));
+        }
+        finally {
+            WaitCursorManager.removeWaitCursor(token);
+            resetStatusMessage();
+            isExportingSnapshot = false;
+        }
 
     }
 
+
     public void createSnapshotNonInteractive(File file) {
-        createSnapshotNonInteractive(getContentPane(), file);
+        createSnapshotNonInteractive(centerPanel, file);
     }
 
     protected void createSnapshotNonInteractive(Component target, File file) {
 
+        log.debug("Creating snapshot: " + file.getName());
+
         String extension = GenericUtilities.getFileExtension(file.getAbsolutePath());
 
         // Use default extension if file has none
         if (extension == null) {
 
-            FileFilter filter = (FileFilter) snapshotFileChooser.getFileFilter();
+            FileFilter filter = snapshotFileChooser.getFileFilter();
 
             // Figure out the proper extension
             if (!(filter instanceof SnapshotFileFilter)) {
@@ -2101,18 +1771,20 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
         // If valid extension
         if (type != SnapshotFileType.NULL) {
-            // Take the snapshot
+
+            boolean doubleBuffered = RepaintManager.currentManager(getContentPane()).isDoubleBufferingEnabled();
             try {
+                isExportingSnapshot = true;
                 RepaintManager.currentManager(getContentPane()).setDoubleBufferingEnabled(false);
                 doComponentSnapshot(target, file, type);
 
             } finally {
-                if (!IGVConstants.IS_MAC) {
-                    RepaintManager.currentManager(getContentPane()).setDoubleBufferingEnabled(true);
-                }
-
+                RepaintManager.currentManager(getContentPane()).setDoubleBufferingEnabled(doubleBuffered);
+                isExportingSnapshot = false;
             }
         }
+
+        log.debug("Finished creating snapshot: " + file.getName());
     }
 
     public File selectSnapshotFile(
@@ -2121,11 +1793,9 @@ public class IGVMainFrame extends javax.swing.JFrame {
         SnapshotFileFilter snapshotFileFilter = null;
         if (defaultFile != null) {
 
-            String fileExtension =
-                    GenericUtilities.getFileExtension(defaultFile.getAbsolutePath());
-            snapshotFileFilter =
-                    GenericUtilities.getSnapshotFileFilterForType(
-                            GenericUtilities.getSnapshotFileType(fileExtension));
+            String fileExtension = GenericUtilities.getFileExtension(defaultFile.getAbsolutePath());
+            snapshotFileFilter = GenericUtilities.getSnapshotFileFilterForType(
+                    GenericUtilities.getSnapshotFileType(fileExtension));
         }
 
         snapshotFileChooser.setFileFilter(snapshotFileFilter);
@@ -2158,16 +1828,15 @@ public class IGVMainFrame extends javax.swing.JFrame {
             log.debug("Setting current genome id");
         }
 
-        String currentGenomeId = IGVModel.getInstance().getViewContext().getGenomeId();
+        String currentGenomeId = session.getViewContext().getGenomeId();
         if (currentGenomeId != null && id.equalsIgnoreCase(currentGenomeId)) {
             // Nothing to do if genome already loaded
             return;
         }
 
-        String gid = model.getViewContext().setGenomeId(id);
+        String gid = getViewContext().setGenomeId(id);
         FeatureDB.clearFeatures();
-        TrackManager.getInstance().loadGeneTrack(gid);
-        clearImageCacheWithNoRepaint();
+        IGVMainFrame.getInstance().getTrackManager().loadGeneTrack(gid);
 
 
         if (igvCommandBar != null) {
@@ -2179,19 +1848,11 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
     private void createZoomCursors() throws HeadlessException, IndexOutOfBoundsException {
         if (zoomInCursor == null || zoomOutCursor == null) {
-            final Image zoomInImage =
-                    ((ImageIcon) IconFactory.getInstance().getIcon(
-                            IconFactory.IconID.ZOOM_IN)).getImage();
-            final Image zoomOutImage =
-                    ((ImageIcon) IconFactory.getInstance().getIcon(
-                            IconFactory.IconID.ZOOM_OUT)).getImage();
+            final Image zoomInImage = IconFactory.getInstance().getIcon(IconFactory.IconID.ZOOM_IN).getImage();
+            final Image zoomOutImage = IconFactory.getInstance().getIcon(IconFactory.IconID.ZOOM_OUT).getImage();
             final Point hotspot = new Point(10, 10);
-            zoomInCursor =
-                    getToolkit().createCustomCursor(
-                            zoomInImage, hotspot, "Zoom in");
-            zoomOutCursor =
-                    getToolkit().createCustomCursor(
-                            zoomOutImage, hotspot, "Zoom out");
+            zoomInCursor = getToolkit().createCustomCursor(zoomInImage, hotspot, "Zoom in");
+            zoomOutCursor = getToolkit().createCustomCursor(zoomOutImage, hotspot, "Zoom out");
 
         }
 
@@ -2203,19 +1864,14 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
             // Make backgroun transparent
             Graphics2D g = handImage.createGraphics();
-            g.setComposite(AlphaComposite.getInstance(
-                    AlphaComposite.CLEAR, 0.0f));
+            g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
             Rectangle2D.Double rect = new Rectangle2D.Double(0, 0, 32, 32);
             g.fill(rect);
 
             // Draw hand image in middle
-            g =
-                    handImage.createGraphics();
-            g.drawImage(IconFactory.getInstance().getIcon(
-                    IconFactory.IconID.OPEN_HAND).getImage(), 0, 0, null);
-            handCursor =
-                    getToolkit().createCustomCursor(
-                            handImage, new Point(8, 6), "Move");
+            g = handImage.createGraphics();
+            g.drawImage(IconFactory.getInstance().getIcon(IconFactory.IconID.OPEN_HAND).getImage(), 0, 0, null);
+            handCursor = getToolkit().createCustomCursor(handImage, new Point(8, 6), "Move");
         }
 
         if (fistCursor == null) {
@@ -2274,39 +1930,52 @@ public class IGVMainFrame extends javax.swing.JFrame {
     }
 
     public void packViews() {
-        ((View) applicationHeaderView).packView();
-        for (TrackSetScrollPane tsv : trackSetScrollPanes.values()) {
-            tsv.getTrackSetView().packView();
+        ((IGVPanel) applicationHeaderView).packView();
+        for (TrackPanelScrollPane tsv : trackManager.getTrackPanelScrollPanes()) {
+            tsv.getTrackPanel().packView();
         }
 
     }
 
-    public void reset() {
+    public void createNewSession(String sessionName) {
+        LRUCache.clearCaches();
+
+        AttributeManager.getInstance().clearAllAttributes();
 
-        attributeCheckList.clear();
-        setCurrentSessionFilePath(null);
-        setTitle(IGVConstants.APPLICATION_NAME);
+        setTitle(UIConstants.APPLICATION_NAME);
 
         if (filterTracksAction != null) {
             filterTracksAction.resetTrackFilter();
         }
 
         AttributeManager.getInstance().clearAllAttributes();
-        IGVModel.getInstance().clearRegionsOfInterest();
-        TrackManager.getInstance().resetApplication();
+        session = new Session(sessionName);
+
 
         // Remove user added panels
-        for (TrackSetScrollPane tsp : trackSetScrollPanes.values()) {
+        for (TrackPanelScrollPane tsp : trackManager.getTrackPanelScrollPanes()) {
+            tsp.getTrackPanel().clearTracks();
             if (tsp == dataTrackScrollPane || tsp == featureTrackScrollPane) {
                 continue;
             }
             centerSplitPane.remove(tsp);
+            TrackNamePanel.removeDropListenerFor(tsp.getNamePanel());
+
         }
 
-        trackSetScrollPanes.clear();
-        trackSetScrollPanes.put(DATA_PANEL_NAME, (TrackSetScrollPane) dataTrackScrollPane);
-        if (!TrackManager.getInstance().isSinglePanel()) {
-            trackSetScrollPanes.put(FEATURE_PANEL_NAME, (TrackSetScrollPane) featureTrackScrollPane);
+        trackManager.reset();
+        trackManager.clearScrollPanes();
+        trackManager.putScrollPane(TrackManager.DATA_PANEL_NAME, dataTrackScrollPane);
+        Track geneTrack = trackManager.getGeneTrack();
+        if (PreferenceManager.getInstance().isShowSingleTrackPane()) {
+            if (geneTrack != null) {
+                dataTrackScrollPane.getTrackPanel().addTrack(geneTrack);
+            }
+        } else {
+            trackManager.putScrollPane(TrackManager.FEATURE_PANEL_NAME, featureTrackScrollPane);
+            if (geneTrack != null) {
+                featureTrackScrollPane.getTrackPanel().addTrack(geneTrack);
+            }
         }
 
         doResizeTrackPanels();
@@ -2338,53 +2007,32 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
         try {
             PreferenceManager.getInstance().clear();
-            boolean isShow =
-                    PreferenceManager.getInstance().getShowAttributeView();
-            boolean isShowROI =
-                    PreferenceManager.getInstance().getShowRegionTool();
-
+            boolean isShow = PreferenceManager.getInstance().getShowAttributeView();
             doShowAttributeDisplay(isShow);
-
             doRefresh();
 
         } catch (Exception e) {
             String message = "Failure while resetting preferences!";
-            showAndLogErrorMessage(IGVMainFrame.theInstance, message, log, e);
+            MessageUtils.showAndLogErrorMessage(IGVMainFrame.theInstance, message, log, e);
         }
 
     }
 
     public void updateTrackState() {
-        clearImageCacheWithNoRepaint();
-        for (TrackSetScrollPane sp : trackSetScrollPanes.values()) {
+        for (TrackPanelScrollPane sp : trackManager.getTrackPanelScrollPanes()) {
             sp.getDataPanel().doResize();
         }
-
         doRefresh();
-
-    }
-
-    public TrackFilter getTrackFilter() {
-
-        if (filterTracksAction == null) {
-            return null;
-        } else {
-            return filterTracksAction.getTrackFilter();
-        }
-
     }
 
-    public void setTrackFilter(TrackFilter trackFilter) {
 
+    public void updateTrackFilter() {
         if (filterTracksAction != null) {
             // TODO -- HOLDING THIS STATE ON THE ACTION OBJECT SEEMS BAD
-            filterTracksAction.setTrackFilter(trackFilter);
+            filterTracksAction.updateTrackFilter();
             // Update the state of the current tracks
             updateTrackState();
-
         }
-
-
     }
 
     public void setFilterMatchAll(boolean value) {
@@ -2429,27 +2077,23 @@ public class IGVMainFrame extends javax.swing.JFrame {
     /**
      * Add a new data panel set
      */
-    public void addDataPanel(String name) {
-        TrackSetView tsv = new TrackSetView(name);
-        final TrackSetScrollPane sp = new TrackSetScrollPane();
-        sp.setViewportView(tsv);
-        sp.setPreferredSize(new Dimension(700, 300));
-        AttributeManager.getInstance().addPropertyChangeListener(sp.getAttributePanel());
-
-        for (TrackSetScrollPane tsp : trackSetScrollPanes.values()) {
+    public TrackPanelScrollPane addDataPanel(String name) {
+
+        TrackPanel trackPanel = new TrackPanel(name);
+        final TrackPanelScrollPane sp = new TrackPanelScrollPane();
+        sp.setViewportView(trackPanel);
+        //sp.setPreferredSize(new Dimension(700, 300));
+
+        for (TrackPanelScrollPane tsp : trackManager.getTrackPanelScrollPanes()) {
             tsp.minimizeHeight();
         }
 
-        trackSetScrollPanes.put(name, sp);
-        TrackManager.getInstance().initGroupsForPanel(name);
+        trackManager.putScrollPane(name, sp);
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
                 // TODO Resize the data panel to make as much space as possible
-                //DataPanel dp = ((TrackSetScrollPane) dataTrackScrollPane).getDataPanel();
-                //dp.doResize();
-
 
                 // Insert the new panel just before the feature panel, or at the end if there is no feature panel.
                 int featurePaneIdx = centerSplitPane.indexOfPane(featureTrackScrollPane);
@@ -2459,35 +2103,83 @@ public class IGVMainFrame extends javax.swing.JFrame {
                     centerSplitPane.add(sp);
                 }
 
-// Resize data panel, unless single pane
                 if (!PreferenceManager.getInstance().isShowSingleTrackPane()) {
-                    if (TrackManager.getInstance().getTracksForPanel(IGVMainFrame.DATA_PANEL_NAME).size() == 0) {
+                    if (sp.getTrackPanel().getTracks().size() == 0) {
                         centerSplitPane.setDividerLocation(0, 3);
-                    } else {
-                        // TrackSetScrollPane dsp = trackSetScrollPanes.get(IGVMainFrame.DATA_PANEL_NAME);
-                        // int dh = Math.max(50, dsp.getDataPanel().getHeight());
-                        // int dph = dsp.getTrackSetView().getPreferredPanelHeight();
-                        // int nh = Math.min(dh, Math.max(50, dph));
                     }
                 }
 
                 packViews();
             }
         });
+
+        return sp;
+    }
+
+    /**
+     * Return an ordered list of track panels.  This method is provided primarily for storing sessions, where
+     * the track panels need to be stored in order.
+     */
+    public List<TrackPanel> getTrackPanels() {
+        ArrayList panels = new ArrayList();
+        for (Component c : centerSplitPane.getComponents()) {
+            if (c instanceof TrackPanelScrollPane) {
+                panels.add(((TrackPanelScrollPane) c).getTrackPanel());
+            }
+        }
+        return panels;
+    }
+
+    public void tweakPanelDivider() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
+
+            public void run() {
+                // TODO Resize the data panel to make as much space as possible
+                int h = centerSplitPane.getHeight();
+                int nPanes = centerSplitPane.getPaneCount();
+
+                double prefHeight = 0;
+                for (int i = 0; i < nPanes; i++) {
+                    prefHeight += centerSplitPane.getPaneAt(i).getPreferredSize().getHeight();
+                }
+                double ratio = h / prefHeight;
+                int pos = 0;
+                for (int i = 0; i < nPanes - 1; i++) {
+
+                    pos += (int) (ratio * centerSplitPane.getPaneAt(i).getPreferredSize().getHeight());
+                    centerSplitPane.setDividerLocation(i, pos);
+                }
+                packViews();
+            }
+        });
+
     }
 
     public void removeDataPanel(String name) {
-        TrackSetScrollPane sp = trackSetScrollPanes.get(name);
+        TrackPanelScrollPane sp = trackManager.getScrollPane(name);
+        // Don't remove the "special" panes
+        if (sp == dataTrackScrollPane || sp == featureTrackScrollPane) {
+            return;
+        }
         if (sp != null) {
             centerSplitPane.remove(sp);
-            AttributeManager.getInstance().removePropertyChangeListener(sp.getAttributePanel());
-            trackSetScrollPanes.remove(name);
+            trackManager.removeScrollPane(name);
+            TrackNamePanel.removeDropListenerFor(sp.getNamePanel());
         }
+    }
 
+    public TrackPanel getDataPanel(String name) {
+        TrackPanelScrollPane sp = trackManager.getScrollPane(name);
+        if (sp == null) {
+            sp = addDataPanel(name);
+            trackManager.putScrollPane(name, sp);
+        }
+        return sp.getTrackPanel();
     }
 
+
     public boolean scrollToTrack(String trackName) {
-        for (TrackSetScrollPane sp : trackSetScrollPanes.values()) {
+        for (TrackPanelScrollPane sp : trackManager.getTrackPanelScrollPanes()) {
             if (sp.getNamePanel().scrollTo(trackName)) {
                 return true;
             }
@@ -2500,7 +2192,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
         setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
 
-        infoPanel = new InfoPanel();
+        nameHeaderPanel = new NameHeaderPanel();
         attributeHeaderPanel = new AttributeHeaderPanel();
         cytobandPanel = new CytobandPanel();
         rulerPanel = new RulerPanel();
@@ -2542,7 +2234,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
         headerPanel.add(rulerPanel, java.awt.BorderLayout.CENTER);
 
 
-        applicationHeaderView = new View();
+        applicationHeaderView = new IGVPanel();
         applicationHeaderView.setDebugGraphicsOptions(javax.swing.DebugGraphics.NONE_OPTION);
         applicationHeaderView.setMaximumSize(new java.awt.Dimension(2147483647, 110));
         applicationHeaderView.setMinimumSize(new java.awt.Dimension(1000, 135));
@@ -2556,26 +2248,26 @@ public class IGVMainFrame extends javax.swing.JFrame {
         attributeHeaderPanel.setPreferredSize(new java.awt.Dimension(0, 0));
         attributeHeaderPanel.setLayout(new java.awt.GridLayout(1, 3));
 
-        infoPanel.setBackground(new java.awt.Color(255, 255, 255));
-        infoPanel.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
-        infoPanel.setDebugGraphicsOptions(javax.swing.DebugGraphics.NONE_OPTION);
-        infoPanel.setMinimumSize(new java.awt.Dimension(0, 0));
-        infoPanel.setPreferredSize(new java.awt.Dimension(0, 0));
-        infoPanel.setLayout(null);
+        nameHeaderPanel.setBackground(new java.awt.Color(255, 255, 255));
+        nameHeaderPanel.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
+        nameHeaderPanel.setDebugGraphicsOptions(javax.swing.DebugGraphics.NONE_OPTION);
+        nameHeaderPanel.setMinimumSize(new java.awt.Dimension(0, 0));
+        nameHeaderPanel.setPreferredSize(new java.awt.Dimension(0, 0));
+        nameHeaderPanel.setLayout(null);
 
-        applicationHeaderView.add(infoPanel, new org.netbeans.lib.awtextra.AbsoluteConstraints(0, 0, 150, 125));
+        applicationHeaderView.add(nameHeaderPanel, new org.netbeans.lib.awtextra.AbsoluteConstraints(0, 0, 150, 125));
         applicationHeaderView.add(attributeHeaderPanel, new org.netbeans.lib.awtextra.AbsoluteConstraints(160, 0, 120, 125));
 
         regionOfInterestPane.setBackground(new java.awt.Color(255, 255, 255));
         regionOfInterestPane.setDebugGraphicsOptions(javax.swing.DebugGraphics.NONE_OPTION);
         regionOfInterestPane.setMinimumSize(new java.awt.Dimension(0, 13));
         regionOfInterestPane.setOpaque(false);
-        GroupLayout regionOfInterestPaneLayout = new org.jdesktop.layout.GroupLayout(regionOfInterestPane);
-        regionOfInterestPane.setLayout(regionOfInterestPaneLayout);
-        regionOfInterestPaneLayout.setHorizontalGroup(
-                regionOfInterestPaneLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING).add(0, 708, Short.MAX_VALUE));
-        regionOfInterestPaneLayout.setVerticalGroup(
-                regionOfInterestPaneLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING).add(0, 10, Short.MAX_VALUE));
+        //GroupLayout regionOfInterestPaneLayout = new org.jdesktop.layout.GroupLayout(regionOfInterestPane);
+        //regionOfInterestPane.setLayout(regionOfInterestPaneLayout);
+        //regionOfInterestPaneLayout.setHorizontalGroup(
+        //        regionOfInterestPaneLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING).add(0, 708, Short.MAX_VALUE));
+        //regionOfInterestPaneLayout.setVerticalGroup(
+        //        regionOfInterestPaneLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING).add(0, 10, Short.MAX_VALUE));
 
         headerPanel.add(regionOfInterestPane, java.awt.BorderLayout.SOUTH);
 
@@ -2588,8 +2280,8 @@ public class IGVMainFrame extends javax.swing.JFrame {
         centerPanel.setLayout(new java.awt.BorderLayout());
         centerPanel.add(headerScrollPane, java.awt.BorderLayout.NORTH);
 
-        dataTrackScrollPane = new TrackSetScrollPane();
-        JPanel dataTrackView = new TrackSetView(DATA_PANEL_NAME);
+        dataTrackScrollPane = new TrackPanelScrollPane();
+        JPanel dataTrackView = new TrackPanel(TrackManager.DATA_PANEL_NAME);
 
 
         dataTrackScrollPane.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(102, 102, 102)));
@@ -2603,14 +2295,13 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
         dataTrackView.setMinimumSize(new java.awt.Dimension(1000, 0));
         dataTrackView.setPreferredSize(new java.awt.Dimension(1000, 345));
-        dataTrackView.setLayout(null);
         dataTrackScrollPane.setViewportView(dataTrackView);
 
         centerPanel.add(dataTrackScrollPane, java.awt.BorderLayout.CENTER);
 
         if (!PreferenceManager.getInstance().isShowSingleTrackPane()) {
-            featureTrackScrollPane = new TrackSetScrollPane();
-            JPanel featureTrackView = new TrackSetView(FEATURE_PANEL_NAME);
+            featureTrackScrollPane = new TrackPanelScrollPane();
+            JPanel featureTrackView = new TrackPanel(TrackManager.FEATURE_PANEL_NAME);
             featureTrackScrollPane.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(102, 102, 102)));
             featureTrackScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
             featureTrackScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
@@ -2624,7 +2315,6 @@ public class IGVMainFrame extends javax.swing.JFrame {
             featureTrackView.setDebugGraphicsOptions(javax.swing.DebugGraphics.NONE_OPTION);
             featureTrackView.setMinimumSize(new java.awt.Dimension(1000, 0));
             featureTrackView.setPreferredSize(new java.awt.Dimension(1000, 115));
-            featureTrackView.setLayout(null);
             featureTrackScrollPane.setViewportView(featureTrackView);
 
             centerPanel.add(featureTrackScrollPane, java.awt.BorderLayout.SOUTH);
@@ -2640,73 +2330,49 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
     }
 
-    public String getCurrentSessionFilePath() {
-        return currentSessionFilePath;
 
+    public Session getSession() {
+        return session;
     }
 
-    public void setCurrentSessionFilePath(String sessionFilePath) {
-        if (sessionFilePath == null) {
-            currentSessionFilePath = null;
-            setTitle(IGVConstants.APPLICATION_NAME);
-        } else {
-            currentSessionFilePath = sessionFilePath.trim();
-            setTitle(IGVConstants.APPLICATION_NAME + " - Session: " + currentSessionFilePath);
-        }
-
-    }
-
-
     final public void doRestoreSession(final File sessionFile,
-                                       final String locus,
-                                       boolean doInBackgrond) {
+                                       final String locus) {
 
         String filePath = "";
         if (sessionFile != null) {
 
-            Runnable runnable = new Runnable() {
+            log.debug("Run doRestoreSession");
 
-                public void run() {
+            InputStream inputStream = null;
+            CursorToken token = WaitCursorManager.showWaitCursor();
+            try {
+                inputStream = new BufferedInputStream(new FileInputStream(sessionFile));
+                doRestoreSession(inputStream, sessionFile.getAbsolutePath(), locus, false);
 
-                    log.debug("Run doRestoreSession");
+                String sessionFilePath = sessionFile.getAbsolutePath();
+                if (!recentSessionList.contains(sessionFilePath)) {
+                    recentSessionList.addFirst(sessionFilePath);
+                }
 
-                    InputStream inputStream = null;
+            } catch (Exception e) {
+                String message = "Failed to load session! : " + sessionFile.getAbsolutePath();
+                MessageUtils.showAndLogErrorMessage(IGVMainFrame.this, message, log, e);
+            } finally {
+                WaitCursorManager.removeWaitCursor(token);
+                if (inputStream != null) {
                     try {
-                        inputStream = new BufferedInputStream(new FileInputStream(sessionFile));
-                        doRestoreSession(inputStream, sessionFile.toString(), locus, false);
-                        String sessionFilePath = sessionFile.getAbsolutePath();
-                        setCurrentSessionFilePath(sessionFilePath);
-                        if (!recentSessionList.contains(sessionFilePath)) {
-                            recentSessionList.addFirst(sessionFilePath);
-                        }
-
-                    } catch (Exception e) {
-                        String message = "Failed to load session! : " + sessionFile.getAbsolutePath();
-                        showAndLogErrorMessage(IGVMainFrame.this, message, log, e);
-                    } finally {
-                        if (inputStream != null) {
-                            try {
-                                inputStream.close();
-                            } catch (IOException iOException) {
-                                log.error("Error closing session stream", iOException);
-                            }
-
-                        }
+                        inputStream.close();
+                    } catch (IOException iOException) {
+                        log.error("Error closing session stream", iOException);
                     }
-                    log.debug("Finish doRestoreSession");
-
                 }
-            };
-
-            if (doInBackgrond) {
-                LongRunningTask.submit(runnable);
-            } else {
-                runnable.run();
             }
+            log.debug("Finish doRestoreSession");
+
 
         } else {
             String message = "Session file does not exist! : " + filePath;
-            showAndLogErrorMessage(IGVMainFrame.this, message, log);
+            MessageUtils.showAndLogErrorMessage(IGVMainFrame.this, message, log);
         }
 
     }
@@ -2728,10 +2394,10 @@ public class IGVMainFrame extends javax.swing.JFrame {
             InputStream inputStream = null;
             try {
                 inputStream = new BufferedInputStream(sessionURL.openStream());
-                doRestoreSession(inputStream, URLDecoder.decode(sessionURL.getFile(), "UTF-8"), locus, false);
+                doRestoreSession(inputStream, sessionURL.toString(), locus, false);
             } catch (Exception e) {
                 String message = "Failed to load session! : " + sessionURL;
-                showAndLogErrorMessage(IGVMainFrame.this, message, log, e);
+                MessageUtils.showAndLogErrorMessage(IGVMainFrame.this, message, log, e);
             } finally {
 
                 if (inputStream != null) {
@@ -2753,7 +2419,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
                 message += sessionURL.getFile();
             }
 
-            showAndLogErrorMessage(IGVMainFrame.this, message, log);
+            MessageUtils.showAndLogErrorMessage(IGVMainFrame.this, message, log);
         }
 
         if (log.isDebugEnabled()) {
@@ -2763,40 +2429,33 @@ public class IGVMainFrame extends javax.swing.JFrame {
     }
 
     final public void doRestoreSession(final InputStream inputStream,
-                                       final String sessionName,
+                                       final String sessionPath,
                                        final String locus,
                                        boolean merge) {
 
         try {
             setStatusBarMessage("Opening session...");
 
-
             if (!merge) {
-                reset(); // Clear everything but the genome
+                createNewSession(sessionPath);
             }
-            Session session = new Session(sessionName);
-
-
-            SessionManager.getInstance().loadSession(inputStream, session, sessionName, merge);
 
+            (new SessionReader()).loadSession(inputStream, session, sessionPath);
             String searchText = locus == null ? session.getLocus() : locus;
 
             // NOTE: Nothing to do if chr == all
-            if (searchText != null && !searchText.equals(IGVConstants.CHR_ALL) && searchText.trim().length() > 0) {
+            if (searchText != null && !searchText.equals(Globals.CHR_ALL) && searchText.trim().length() > 0) {
                 igvCommandBar.searchByLocus(searchText);
-            } else {
-                TrackManager.getInstance().preloadSAMTracks();
-
             }
 
-            setTitle(IGVConstants.APPLICATION_NAME + " - Session: " + sessionName);
+            setTitle(UIConstants.APPLICATION_NAME + " - Session: " + sessionPath);
+            LRUCache.clearCaches();
             doRefresh();
         } catch (Exception e) {
-            String message = "Failed to load session! : " + sessionName;
-            showAndLogErrorMessage(IGVMainFrame.this, message, log, e);
+            String message = "Failed to load session! : " + sessionPath;
+            MessageUtils.showAndLogErrorMessage(IGVMainFrame.this, message, log, e);
         } finally {
 
-
             resetStatusMessage();
         }
 
@@ -2807,7 +2466,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
      */
     public void resetStatusMessage() {
         ((ApplicationStatusBar) statusBar).setMessage("" +
-                TrackManager.getInstance().getTrackCount() + " tracks loaded");
+                IGVMainFrame.getInstance().getTrackManager().getVisibleTrackCount() + " tracks loaded");
 
     }
 
@@ -2821,7 +2480,7 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
     public void showLoadedTrackCount() {
         ((ApplicationStatusBar) statusBar).setMessage("" +
-                TrackManager.getInstance().getTrackCount() +
+                IGVMainFrame.getInstance().getTrackManager().getVisibleTrackCount() +
                 " track(s) currently loaded");
     }
 
@@ -2840,8 +2499,51 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
     }
 
+
+    private void closeWindow(final ProgressBar progressBar) {
+        UIUtilities.invokeOnEventThread(new Runnable() {
+            public void run() {
+                progressBar.close();
+            }
+        });
+    }
+
+    /**
+     * Method provided to jump to a locus synchronously.  Used for port command options
+     *
+     * @param locus
+     */
+    public void goToLocus(String locus) {
+
+        this.igvCommandBar.searchByLocus(locus);
+    }
+
+
+    public TrackManager getTrackManager() {
+        return trackManager;
+    }
+
+    private class MyStatusListener implements StatusListener {
+
+        public void statusChanged(StatusChangeEvent event) {
+            String statusMessage = event.getStatusMessage();
+
+            if (statusMessage != null) {
+                setStatusBarMessage(statusMessage);
+            }
+        }
+    }
+
     public static void main(final String args[]) {
 
+
+        log.info(Globals.applicationString());
+
+        System.setProperty("http.agent", Globals.applicationString());
+
+        FileUtils.addRollingAppenderToRootLogger();
+        log.info("Default User Directory: " + UIConstants.getUserDirectory());
+
         Thread.setDefaultUncaughtExceptionHandler(new DefaultExceptionHandler());
 
 
@@ -2873,7 +2575,9 @@ public class IGVMainFrame extends javax.swing.JFrame {
 
                     frame = new IGVMainFrame();
 
-                    frame.setupInitialGenome(args);
+                    IGVHttpUtils.updateProxySettings();
+
+                    frame.startUp(args);
 
                     KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new GlobalKeyDispatcher());
 
@@ -2891,152 +2595,237 @@ public class IGVMainFrame extends javax.swing.JFrame {
         });
     }
 
-    private void setupInitialGenome(final String[] args) {
+
+    /**
+     * Startup the IGV main window,  then execute batch file if supplied.
+     *
+     * @param args
+     */
+    private void startUp(final String[] args) {
 
         if (log.isDebugEnabled()) {
-            log.debug("setupInitialGenome");
+            log.debug("startUp");
         }
 
-        SwingWorker worker = new SwingWorker() {
+        IGVArgs igvArgs = new IGVArgs(args);
+        SwingWorker worker = new StartupWorker(igvArgs);
+        worker.execute();
 
-            public Object doInBackground() {
 
-                final ProgressMonitor monitor = new ProgressMonitor();
+    }
 
-                final ProgressBar progressBar =
-                        ProgressBar.showProgressDialog(IGVMainFrame.this, "Initializing Genome...", monitor, false);
+    /**
+     * Class to encapsulate IGV command line arguments.
+     */
+    static class IGVArgs {
+        String batchFile = null;
+        String sessionFile = null;
+        String dataFileString = null;
+        String locusString = null;
+        String propertyFile = null;
+        String genomeId = null;
+
+        IGVArgs(String[] args) {
+            parseArgs(args);
+        }
+
+        /**
+         * Parse arguments.  All arguments are optional,  a full set of arguments are
+         * firstArg  locusString  -b batchFile -p preferences
+         */
+        private void parseArgs(String[] args) {
+            CmdLineParser parser = new CmdLineParser();
+            CmdLineParser.Option propertyFileOption = parser.addStringOption('p', "preferences");
+            CmdLineParser.Option batchFileOption = parser.addStringOption('b', "batch");
+            CmdLineParser.Option genomeOption = parser.addStringOption('g', "genome");
+            try {
+                parser.parse(args);
+            } catch (CmdLineParser.IllegalOptionValueException e) {
+                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+            } catch (CmdLineParser.UnknownOptionException e) {
+                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+            }
 
-                monitor.fireProgressChange(10);
+            propertyFile = (String) parser.getOptionValue(propertyFileOption);
+            batchFile = (String) parser.getOptionValue(batchFileOption);
+            genomeId = (String) parser.getOptionValue(genomeOption);
 
-                String genomeId = PreferenceManager.getInstance().getDefaultGenome();
-                setGenomeId(genomeId);
+            String[] nonOptionArgs = parser.getRemainingArgs();
+            if (nonOptionArgs != null && nonOptionArgs.length > 0) {
+                String firstArg = nonOptionArgs[0];
+                if (firstArg.endsWith("xml")) {
+                    sessionFile = firstArg;
+                } else {
+                    dataFileString = firstArg;
+                }
+                if (nonOptionArgs.length > 1) {
+                    locusString = nonOptionArgs[1];
+                }
+            }
 
-                String chrName = PreferenceManager.getInstance().getLastChromosomeViewed();
-                model.getViewContext().setChromosomeName(chrName);
+        }
+    }
 
-                monitor.fireProgressChange(50);
 
-                genomeId = model.getViewContext().getGenomeId();
-                try {
-                    igvCommandBar.initializeGenomeList(monitor);
-                    igvCommandBar.selectGenomeFromListWithNoImport(genomeId);
-                } catch (FileNotFoundException ex) {
-                    JOptionPane.showMessageDialog(IGVMainFrame.this, "Error initializing genome list: " + ex.getMessage());
-                    log.error("Error initializing genome list: ", ex);
-                } catch (NoRouteToHostException ex) {
-                    JOptionPane.showMessageDialog(IGVMainFrame.this, "Network error initializing genome list: " + ex.getMessage());
-                    log.error("Network error initializing genome list: ", ex);
-                }
+    /**
+     * Swing worker class to startup IGV
+     */
+    public class StartupWorker extends SwingWorker {
+        IGVArgs igvArgs;
+
+        StartupWorker(IGVArgs args) {
+            this.igvArgs = args;
 
-                // Done
-                closeWindow(progressBar);
+        }
 
-                //If there is an argument assume it is a session file or url
-                if (args.length > 0) {
 
-                    if (log.isDebugEnabled()) {
-                        log.debug("Loadding session data");
-                    }
+        /**
+         * Do the actual work
+         *
+         * @return
+         * @throws Exception
+         */
+        @Override
+        protected Object doInBackground() throws Exception {
 
-                    final IndefiniteProgressMonitor indefMonitor = new IndefiniteProgressMonitor(60);
-                    final ProgressBar bar2 = ProgressBar.showProgressDialog(IGVMainFrame.this,
-                            "Loading session data", indefMonitor, false);
+            final ProgressMonitor monitor = new ProgressMonitor();
+            final ProgressBar progressBar =
+                    ProgressBar.showProgressDialog(IGVMainFrame.this, "Initializing Genome...", monitor, false);
+            monitor.fireProgressChange(10);
 
-                    final String sessionRef = args[0];
+            // Load the last genome and chromosome
+            String genomeId = PreferenceManager.getInstance().getDefaultGenome();
 
-                    // Second argument should be a locus string
-                    final String locusString = (args.length > 1) ? args[1] : null;
+            if (genomeId == null) {
+                genomeId = GenomeManager.getInstance().getTopGenomeListItem().getId();
+            }
+            setGenomeId(genomeId);
 
-                    indefMonitor.start();
-                    try {
+            monitor.fireProgressChange(50);
 
-                        if (log.isDebugEnabled()) {
-                            log.debug("Calling restore session");
-                        }
+            genomeId = getViewContext().getGenomeId(); // <= might have changed
+            try {
+                igvCommandBar.initializeGenomeList(monitor);
+                igvCommandBar.selectGenomeFromListWithNoImport(genomeId);
+            } catch (FileNotFoundException ex) {
+                JOptionPane.showMessageDialog(IGVMainFrame.this, "Error initializing genome list: " + ex.getMessage());
+                log.error("Error initializing genome list: ", ex);
+            } catch (NoRouteToHostException ex) {
+                JOptionPane.showMessageDialog(IGVMainFrame.this, "Network error initializing genome list: " + ex.getMessage());
+                log.error("Network error initializing genome list: ", ex);
+            }
 
-                        // Assume xml files are session files
-                        if (sessionRef.endsWith("xml")) {
-                            if (sessionRef.startsWith("http:") || sessionRef.startsWith("file:")) {
-                                URL url = new URL(sessionRef);
-                                doRestoreSession(url, locusString);
-                            } else {
-                                File sessionFile = new File(sessionRef);
-                                if (sessionFile.exists()) {
-                                    doRestoreSession(sessionFile, locusString, false);
-                                }
-                            }
-                        } else {
-                            // Not an xml file, assume its a data file
-                            String[] tokens = sessionRef.split(",");
-                            List<ResourceLocator> locators = new ArrayList();
-                            for (String p : tokens) {
-                                locators.add(new ResourceLocator(p));
-                            }
-                            TrackManager.getInstance().loadResources(locators);
-                        }
+            // Done
+            closeWindow(progressBar);
 
-                        if (locusString != null) {
-                            goToLocus(locusString);
-                        }
+            if (igvArgs.propertyFile != null) {
 
-                    } catch (Exception ex) {
-                        JOptionPane.showMessageDialog(IGVMainFrame.this, "<html>Error loading session: " + sessionRef + "<br>" + ex.toString());
-                        log.error("Error loading session: " + sessionRef, ex);
-                    }
+            }
 
+            //If there is an argument assume it is a session file or url
+            if (igvArgs.sessionFile != null || igvArgs.dataFileString != null) {
 
-                    indefMonitor.stop();
-                    closeWindow(bar2);
+                System.out.println("Session file = " + igvArgs.sessionFile);
+                System.out.println("Locus = " + igvArgs.locusString);
 
+                if (log.isDebugEnabled()) {
+                    log.debug("Loadding session data");
                 }
-                GuiUtilities.invokeOnEventThread(new Runnable() {
 
-                    public void run() {
-                        setVisible(true);
+                final IndefiniteProgressMonitor indefMonitor = new IndefiniteProgressMonitor(60);
+                final ProgressBar bar2 = ProgressBar.showProgressDialog(IGVMainFrame.this, "Loading session data", indefMonitor, false);
+
+                int idx = 0;
+
+
+                indefMonitor.start();
+                try {
+
+                    if (log.isDebugEnabled()) {
+                        log.debug("Calling restore session");
                     }
-                });
-                return null;
-            }
-        };
 
 
-        worker.execute();
+                    if (igvArgs.genomeId != null) {
+                        selectGenomeFromList(igvArgs.genomeId);
+                    }
 
-    }
+                    if (igvArgs.sessionFile != null) {
+                        log.info("Loading session " + igvArgs.sessionFile);
+                        if (igvArgs.sessionFile.startsWith("http:") ||
+                                igvArgs.sessionFile.startsWith("https:") ||
+                                igvArgs.sessionFile.startsWith("file:")) {
+                            URL url = new URL(igvArgs.sessionFile);
+                            doRestoreSession(url, igvArgs.locusString);
+                        } else {
+                            File sf = new File(igvArgs.sessionFile);
+                            if (sf.exists()) {
+                                doRestoreSession(sf, igvArgs.locusString);
+                            }
+                        }
+                        doRefresh();
+                    } else if (igvArgs.dataFileString != null) {
+                        // Not an xml file, assume its a list of data files
+                        log.info("Loading session " + igvArgs.dataFileString);
+                        String[] tokens = igvArgs.dataFileString.split(",");
+                        List<ResourceLocator> locators = new ArrayList();
+                        for (String p : tokens) {
+                            locators.add(new ResourceLocator(p));
+                        }
+                        getTrackManager().loadResources(locators);
+                        doRefresh();
+                    }
 
-    private void closeWindow(final ProgressBar progressBar) {
-        GuiUtilities.invokeOnEventThread(new Runnable() {
-            public void run() {
-                progressBar.close();
-            }
-        });
-    }
+                    if (igvArgs.locusString != null) {
+                        log.info("Goto " + igvArgs.locusString);
+                        goToLocus(igvArgs.locusString);
+                    }
 
-    /**
-     * Method provided to jump to a locus synchronously.  Used for port command options
-     *
-     * @param locus
-     */
-    public void goToLocus(String locus) {
+                } catch (Exception ex) {
+                    String tmp = igvArgs.sessionFile != null ? igvArgs.sessionFile : igvArgs.dataFileString;
+                    JOptionPane.showMessageDialog(IGVMainFrame.this, "<html>Error loading session: " + tmp + "<br>" + ex.toString());
+                    log.error("Error loading session: " + tmp, ex);
+                }
 
-        this.igvCommandBar.searchByLocus(locus, false);
-    }
 
-    /**
-     * @return the trackSetScrollPanes
-     */
-    public Collection<TrackSetScrollPane> getTrackSetScrollPanes() {
-        return trackSetScrollPanes.values();
-    }
+                indefMonitor.stop();
+                closeWindow(bar2);
+            } else {
+                String chrName = PreferenceManager.getInstance().getLastChromosomeViewed();
+                //Check that this chromosome exists
+                Genome genome = ViewContext.getInstance().getGenome();
+                if (genome != null) {
+                    if (genome.getChromosome(chrName) != null) {
+                        goToLocus(chrName);
+                    } else {
+                        goToLocus(genome.getHomeChromosome());
+                    }
+                }
 
-    private class MyStatusListener implements StatusListener {
+            }
 
-        public void statusChanged(StatusChangeEvent event) {
-            String statusMessage = event.getStatusMessage();
+            UIUtilities.invokeOnEventThread(new Runnable() {
+                public void run() {
+                    setVisible(true);
+                }
+            });
 
-            if (statusMessage != null) {
-                setStatusBarMessage(statusMessage);
+
+            return null;
+        }
+
+
+        /**
+         * Called when the background thread is complete (IGV window is open and data loaded).
+         */
+        @Override
+        protected void done() {
+            if (igvArgs.batchFile != null) {
+                LongRunningTask.submit(new BatchRunner(igvArgs.batchFile));
             }
         }
+
     }
+
+
 }
diff --git a/src/org/broad/igv/ui/IGVModel.java b/src/org/broad/igv/ui/IGVModel.java
deleted file mode 100644
index fa9bff8..0000000
--- a/src/org/broad/igv/ui/IGVModel.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * Class to hold state information that is, potentially, shared by multiple
- * components.  An attempt to reduce the bloat of IGVMainFrame.
- * 
- * A static class.  Could be a singleton, is there any advantage one way or the other?
- */
-package org.broad.igv.ui;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/**
- * @author jrobinso
- */
-public class IGVModel {
-
-    /**
-     * Supports a state machine for the application. States are
-     * INITIAL:  No data tracks loaded
-     * GENOME:   Data loaded, whole genome view
-     * CHROMOSOME:  Data loaded, viewing chromosome
-     */
-    enum ApplicationState {
-
-        INITIAL, GENOME, CHROMOSOME, CHROMOSOME_ZOOMED
-    }
-
-    ;
-    private static IGVModel instance;
-    private ViewContext viewContext = new ViewContext();
-    /**
-     * Map of chromosome -> regions of interest
-     */
-    private Map<String, Collection<RegionOfInterest>> regionsOfInterest = new LinkedHashMap();
-    private String infoText = "";
-
-    private IGVModel() {
-    }
-
-    public static IGVModel getInstance() {
-        if (instance == null) {
-            instance = new IGVModel();
-
-        }
-        return instance;
-    }
-
-    public String getInfoText() {
-        return infoText;
-    }
-
-    public void setInfoText(String aInfoText) {
-        infoText = aInfoText;
-    }
-
-    public ViewContext getViewContext() {
-        return viewContext;
-    }
-
-    public Collection<RegionOfInterest> getRegionsOfInterest(String chr) {
-        return regionsOfInterest.get(chr);
-    }
-
-    public Collection<RegionOfInterest> getAllRegionsOfInterest() {
-        ArrayList<RegionOfInterest> roiList = new ArrayList();
-        for (Collection<RegionOfInterest> roi : regionsOfInterest.values()) {
-            roiList.addAll(roi);
-        }
-        return roiList;
-    }
-
-    public void addRegionOfInterestWithNoListeners(RegionOfInterest roi) {
-        String chr = roi.getChromosomeName();
-        Collection<RegionOfInterest> roiList = regionsOfInterest.get(chr);
-        if (roiList == null) {
-            roiList = new ArrayList();
-            regionsOfInterest.put(chr, roiList);
-        }
-        roiList.add(roi);
-    }
-
-    public void clearRegionsOfInterest() {
-        if (regionsOfInterest != null) {
-            regionsOfInterest.clear();
-        }
-    }
-}
diff --git a/src/org/broad/igv/ui/IGVTool.java b/src/org/broad/igv/ui/IGVTool.java
index dca7fcd..ea80fe9 100644
--- a/src/org/broad/igv/ui/IGVTool.java
+++ b/src/org/broad/igv/ui/IGVTool.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -24,6 +24,7 @@
 package org.broad.igv.ui;
 
 
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.ui.util.Tool;
 
 import java.awt.*;
@@ -56,7 +57,7 @@ abstract public class IGVTool implements Tool {
      * @return
      */
     public ViewContext getViewContext() {
-        return IGVModel.getInstance().getViewContext();
+        return ViewContext.getInstance();
     }
 
     public Component getOwner() {
diff --git a/src/org/broad/igv/ui/LongRunningTask.java b/src/org/broad/igv/ui/LongRunningTask.java
deleted file mode 100644
index 503c85d..0000000
--- a/src/org/broad/igv/ui/LongRunningTask.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-package org.broad.igv.ui;
-
-import org.broad.igv.ui.WaitCursorManager.CursorToken;
-import org.apache.log4j.Logger;
-
-import java.util.concurrent.*;
-
-/**
- * @author jrobinso
- */
-public class LongRunningTask implements Callable {
-
-    private static Logger log = Logger.getLogger(LongRunningTask.class);
-
-    private static ExecutorService threadExecutor = Executors.newFixedThreadPool(3);
-    private static ScheduledExecutorService schedule = Executors.newScheduledThreadPool(1);
-
-    Runnable runnable;
-
-    public static Future submit(Runnable runnable) {
-        log.debug("Submit runnable");
-        return threadExecutor.submit(new LongRunningTask(runnable));
-    }
-
-    public static Future schedule(Runnable runnable, long time) {
-        return schedule.schedule(new LongRunningTask(runnable), time, TimeUnit.MILLISECONDS);
-    }
-
-    public LongRunningTask(Runnable runnable) {
-        this.runnable = runnable;
-    }
-
-    public Object call() throws Exception {
-
-        CursorToken token = WaitCursorManager.showWaitCursor();
-        try {
-            runnable.run();
-            return "";
-        } finally {
-            WaitCursorManager.removeWaitCursor(token);
-        }
-
-    }
-}
-
diff --git a/src/org/broad/igv/ui/MacCompatibleFileChooser.java b/src/org/broad/igv/ui/MacCompatibleFileChooser.java
index 6ceb1b4..db83dcd 100644
--- a/src/org/broad/igv/ui/MacCompatibleFileChooser.java
+++ b/src/org/broad/igv/ui/MacCompatibleFileChooser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -21,8 +21,6 @@ package org.broad.igv.ui;
 
 //~--- non-JDK imports --------------------------------------------------------
 
-import org.broad.igv.IGVConstants;
-
 import javax.swing.*;
 import java.awt.*;
 import java.io.File;
@@ -89,7 +87,7 @@ public class MacCompatibleFileChooser {
             parent = JOptionPane.getRootFrame();
         }
 
-        if (IGVConstants.IS_MAC && ((parent instanceof Frame) || (parent instanceof Dialog))) {
+        if (UIConstants.IS_MAC && ((parent instanceof Frame) || (parent instanceof Dialog))) {
             return showAwtFileDialog(parent, FileDialog.LOAD, directory, title, false);
         }
 
@@ -146,7 +144,7 @@ public class MacCompatibleFileChooser {
     public static File showSaveDialog(Container parent, File selectedDirectory, File selectedFile,
                                       String[] extensions, String dialogTitle) {
 
-        if (IGVConstants.IS_MAC && ((parent instanceof Frame) || (parent instanceof Dialog))) {
+        if (UIConstants.IS_MAC && ((parent instanceof Frame) || (parent instanceof Dialog))) {
             return ensureCorrectExtension(parent,
                     showAwtFileDialog(parent, FileDialog.SAVE,
                             selectedDirectory, selectedFile, dialogTitle, false), extensions,
@@ -190,7 +188,7 @@ public class MacCompatibleFileChooser {
      */
     public static File showSelectDirectoryDialog(Container parent, File selectedDirectory,
                                                  String dialogTitle) {
-        if (IGVConstants.IS_MAC && ((parent instanceof Frame) || (parent instanceof Dialog))) {
+        if (UIConstants.IS_MAC && ((parent instanceof Frame) || (parent instanceof Dialog))) {
             return showAwtFileDialog(parent, FileDialog.SAVE, selectedDirectory, dialogTitle, true);
         }
 
diff --git a/src/org/broad/igv/ui/MessageCollection.java b/src/org/broad/igv/ui/MessageCollection.java
index 28cf584..fe868a3 100644
--- a/src/org/broad/igv/ui/MessageCollection.java
+++ b/src/org/broad/igv/ui/MessageCollection.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/MiscStuff.java b/src/org/broad/igv/ui/MiscStuff.java
index 4d4ec39..11f7499 100644
--- a/src/org/broad/igv/ui/MiscStuff.java
+++ b/src/org/broad/igv/ui/MiscStuff.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/PreferencesEditor.form b/src/org/broad/igv/ui/PreferencesEditor.form
deleted file mode 100644
index d57e9c1..0000000
--- a/src/org/broad/igv/ui/PreferencesEditor.form
+++ /dev/null
@@ -1,1500 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-
-<Form version="1.5" maxVersion="1.5" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
-  <NonVisualComponents>
-    <Component class="javax.swing.ButtonGroup" name="featureStyleButtonGroup">
-    </Component>
-    <Component class="javax.swing.ButtonGroup" name="probeMappingButtonGroup">
-    </Component>
-    <Component class="javax.swing.ButtonGroup" name="rnaiMapapingButtonGroup">
-    </Component>
-  </NonVisualComponents>
-  <Properties>
-    <Property name="defaultCloseOperation" type="int" value="2"/>
-    <Property name="resizable" type="boolean" value="false"/>
-  </Properties>
-  <SyntheticProperties>
-    <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
-  </SyntheticProperties>
-  <AuxValues>
-    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
-    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
-    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="2"/>
-    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
-    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
-    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,2,78,0,0,3,25"/>
-  </AuxValues>
-
-  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
-  <SubComponents>
-    <Container class="javax.swing.JTabbedPane" name="tabbedPane">
-      <AccessibilityProperties>
-        <Property name="AccessibleContext.accessibleName" type="java.lang.String" value="Advanced"/>
-      </AccessibilityProperties>
-      <Constraints>
-        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
-          <BorderConstraints direction="After"/>
-        </Constraint>
-      </Constraints>
-
-      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
-      <SubComponents>
-        <Container class="javax.swing.JPanel" name="generalPanel">
-          <Constraints>
-            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
-              <JTabbedPaneConstraints tabName="General">
-                <Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-                  <PlainString value="General"/>
-                </Property>
-              </JTabbedPaneConstraints>
-            </Constraint>
-          </Constraints>
-
-          <Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
-            <Property name="useNullLayout" type="boolean" value="true"/>
-          </Layout>
-          <SubComponents>
-            <Container class="javax.swing.JPanel" name="jPanel10">
-              <Constraints>
-                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout" value="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout$AbsoluteConstraintsDescription">
-                  <AbsoluteConstraints x="0" y="0" width="610" height="420"/>
-                </Constraint>
-              </Constraints>
-
-              <Layout>
-                <DimensionLayout dim="0">
-                  <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" alignment="0" attributes="0">
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="0" attributes="0">
-                              <Group type="102" alignment="0" attributes="0">
-                                  <EmptySpace min="-2" pref="35" max="-2" attributes="0"/>
-                                  <Component id="missingDataExplanation" min="-2" max="-2" attributes="0"/>
-                              </Group>
-                              <Component id="showMissingDataCB" alignment="0" min="-2" max="-2" attributes="0"/>
-                              <Component id="combinePanelsCB" alignment="0" min="-2" max="-2" attributes="0"/>
-                              <Component id="joinSegmentsCB" alignment="0" min="-2" max="-2" attributes="0"/>
-                              <Component id="searchZoomCB" alignment="0" min="-2" max="-2" attributes="0"/>
-                              <Component id="showAttributesDisplayCheckBox" alignment="0" min="-2" max="-2" attributes="0"/>
-                              <Group type="102" alignment="0" attributes="0">
-                                  <EmptySpace min="-2" pref="44" max="-2" attributes="0"/>
-                                  <Group type="103" groupAlignment="1" attributes="0">
-                                      <Component id="missingDataExplanation7" alignment="1" min="-2" pref="508" max="-2" attributes="0"/>
-                                      <Component id="missingDataExplanation6" alignment="1" min="-2" max="-2" attributes="0"/>
-                                  </Group>
-                              </Group>
-                          </Group>
-                          <EmptySpace pref="39" max="32767" attributes="0"/>
-                      </Group>
-                  </Group>
-                </DimensionLayout>
-                <DimensionLayout dim="1">
-                  <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" attributes="0">
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="showMissingDataCB" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="missingDataExplanation" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace type="separate" max="-2" attributes="0"/>
-                          <Component id="combinePanelsCB" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace min="-2" pref="26" max="-2" attributes="0"/>
-                          <Component id="joinSegmentsCB" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="missingDataExplanation6" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
-                          <Component id="showAttributesDisplayCheckBox" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace min="-2" pref="28" max="-2" attributes="0"/>
-                          <Component id="searchZoomCB" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="missingDataExplanation7" min="-2" pref="78" max="-2" attributes="0"/>
-                          <EmptySpace pref="45" max="32767" attributes="0"/>
-                      </Group>
-                  </Group>
-                </DimensionLayout>
-              </Layout>
-              <SubComponents>
-                <Component class="javax.swing.JLabel" name="missingDataExplanation">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="Lucida Grande" size="12" style="2"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="&lt;html&gt;Distinguish  regions with zero values from regions with no data on plots &lt;br&gt;(e.g. bar charts).  Regions with no data are indicated with a gray background." noResource="true"/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="showMissingDataCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Distinguish Missing Data"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showMissingDataCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="combinePanelsCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Combine Data and Feature Panels"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="combinePanelsCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="joinSegmentsCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Join Adjacent CopyNumber Segments"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="joinSegmentsCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="showAttributesDisplayCheckBox">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Show Attribute Display"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showAttributesDisplayCheckBoxActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="searchZoomCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Zoom to features"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="searchZoomCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JLabel" name="missingDataExplanation6">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="Lucida Grande" size="12" style="2"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="&lt;html&gt;This option applies to segmented copy number data only.  When selected, gaps between&lt;br&gt;adjacent segments are filled by extending segment endpoints." noResource="true"/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JLabel" name="missingDataExplanation7">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="Lucida Grande" size="12" style="2"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="&lt;html&gt;This option controls the behavior of feature searchs.  If true, the zoom level is changed as required to size the view to the feature size.  If false the zoom level is unchanged." noResource="true"/>
-                    <Property name="verticalAlignment" type="int" value="1"/>
-                  </Properties>
-                  <AccessibilityProperties>
-                    <Property name="AccessibleContext.accessibleName" type="java.lang.String" value="&lt;html&gt;This option controls the zoom behavior when using search by feature.  If checked, the zoom level is changed as required to size the view width to exactly match the feature size.  If unchecked the zoom level is not changed."/>
-                  </AccessibilityProperties>
-                </Component>
-              </SubComponents>
-            </Container>
-          </SubComponents>
-        </Container>
-        <Container class="javax.swing.JPanel" name="tracksPanel">
-          <Constraints>
-            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
-              <JTabbedPaneConstraints tabName="Tracks">
-                <Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-                  <PlainString value="Tracks"/>
-                </Property>
-              </JTabbedPaneConstraints>
-            </Constraint>
-          </Constraints>
-
-          <Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
-            <Property name="useNullLayout" type="boolean" value="true"/>
-          </Layout>
-          <SubComponents>
-            <Container class="javax.swing.JPanel" name="jPanel6">
-              <Constraints>
-                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout" value="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout$AbsoluteConstraintsDescription">
-                  <AbsoluteConstraints x="40" y="20" width="690" height="440"/>
-                </Constraint>
-              </Constraints>
-
-              <Layout>
-                <DimensionLayout dim="0">
-                  <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" attributes="0">
-                          <Group type="103" groupAlignment="0" attributes="0">
-                              <Group type="102" attributes="0">
-                                  <EmptySpace max="-2" attributes="0"/>
-                                  <Group type="103" groupAlignment="0" attributes="0">
-                                      <Group type="102" alignment="0" attributes="0">
-                                          <EmptySpace min="-2" pref="44" max="-2" attributes="0"/>
-                                          <Component id="missingDataExplanation3" min="-2" max="-2" attributes="0"/>
-                                      </Group>
-                                      <Group type="102" alignment="0" attributes="0">
-                                          <Component id="jLabel23" min="-2" max="-2" attributes="0"/>
-                                          <EmptySpace type="separate" max="-2" attributes="0"/>
-                                          <Component id="expandCB" min="-2" max="-2" attributes="0"/>
-                                          <EmptySpace min="-2" pref="374" max="-2" attributes="0"/>
-                                      </Group>
-                                      <Group type="102" alignment="0" attributes="0">
-                                          <EmptySpace min="-2" pref="24" max="-2" attributes="0"/>
-                                          <Component id="missingDataExplanation4" min="-2" pref="354" max="-2" attributes="0"/>
-                                      </Group>
-                                      <Group type="102" alignment="0" attributes="0">
-                                          <Component id="trackNameAttributeLabel" min="-2" max="-2" attributes="0"/>
-                                          <EmptySpace max="-2" attributes="0"/>
-                                          <Component id="trackNameAttributeField" min="-2" pref="216" max="-2" attributes="0"/>
-                                      </Group>
-                                      <Group type="102" alignment="0" attributes="0">
-                                          <Component id="jLabel8" min="-2" max="-2" attributes="0"/>
-                                          <EmptySpace min="-2" pref="39" max="-2" attributes="0"/>
-                                          <Component id="defaultTrackHeightField" min="-2" pref="57" max="-2" attributes="0"/>
-                                      </Group>
-                                      <Group type="102" alignment="0" attributes="0">
-                                          <EmptySpace min="-2" pref="24" max="-2" attributes="0"/>
-                                          <Component id="missingDataExplanation5" pref="634" max="32767" attributes="0"/>
-                                      </Group>
-                                      <Group type="102" alignment="0" attributes="0">
-                                          <Component id="jLabel5" min="-2" max="-2" attributes="0"/>
-                                          <EmptySpace min="-2" pref="36" max="-2" attributes="0"/>
-                                          <Component id="defaultChartTrackHeightField" min="-2" pref="57" max="-2" attributes="0"/>
-                                      </Group>
-                                  </Group>
-                              </Group>
-                              <Group type="102" alignment="0" attributes="0">
-                                  <EmptySpace min="-2" pref="63" max="-2" attributes="0"/>
-                                  <Component id="missingDataExplanation2" min="-2" pref="354" max="-2" attributes="0"/>
-                              </Group>
-                          </Group>
-                          <EmptySpace max="-2" attributes="0"/>
-                      </Group>
-                  </Group>
-                </DimensionLayout>
-                <DimensionLayout dim="1">
-                  <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" attributes="0">
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="3" attributes="0">
-                              <Component id="jLabel5" alignment="3" min="-2" max="-2" attributes="0"/>
-                              <Component id="defaultChartTrackHeightField" alignment="3" min="-2" max="-2" attributes="0"/>
-                          </Group>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="missingDataExplanation4" min="-2" pref="25" max="-2" attributes="0"/>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="3" attributes="0">
-                              <Component id="jLabel8" alignment="3" min="-2" max="-2" attributes="0"/>
-                              <Component id="defaultTrackHeightField" alignment="3" min="-2" max="-2" attributes="0"/>
-                          </Group>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="missingDataExplanation5" min="-2" pref="25" max="-2" attributes="0"/>
-                          <EmptySpace min="-2" pref="26" max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="3" attributes="0">
-                              <Component id="trackNameAttributeLabel" alignment="3" min="-2" max="-2" attributes="0"/>
-                              <Component id="trackNameAttributeField" alignment="3" min="-2" max="-2" attributes="0"/>
-                          </Group>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="missingDataExplanation2" min="-2" pref="65" max="-2" attributes="0"/>
-                          <EmptySpace min="-2" pref="32" max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="3" attributes="0">
-                              <Component id="jLabel23" alignment="3" min="-2" max="-2" attributes="0"/>
-                              <Component id="expandCB" alignment="3" min="-2" max="-2" attributes="0"/>
-                          </Group>
-                          <EmptySpace min="-2" pref="11" max="-2" attributes="0"/>
-                          <Component id="missingDataExplanation3" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace pref="82" max="32767" attributes="0"/>
-                      </Group>
-                  </Group>
-                </DimensionLayout>
-              </Layout>
-              <SubComponents>
-                <Component class="javax.swing.JLabel" name="jLabel5">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Default Track Height, Charts (Pixels)" noResource="true"/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JTextField" name="defaultChartTrackHeightField">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="40" noResource="true"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="defaultChartTrackHeightFieldActionPerformed"/>
-                    <EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="defaultChartTrackHeightFieldFocusLost"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JLabel" name="trackNameAttributeLabel">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Track Name Attribute" noResource="true"/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JTextField" name="trackNameAttributeField">
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="trackNameAttributeFieldActionPerformed"/>
-                    <EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="trackNameAttributeFieldFocusLost"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JLabel" name="missingDataExplanation2">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="Lucida Grande" size="12" style="2"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="&lt;html&gt;Name of an attribute to be used to label  tracks.  If provided tracks will be labeled with the corresponding attribute values from the sample information file" noResource="true"/>
-                  </Properties>
-                  <AccessibilityProperties>
-                    <Property name="AccessibleContext.accessibleName" type="java.lang.String" value="&lt;html&gt;Name of an attribute to be used to label  tracks.  If provided   &lt;br&gt;tracks will be labeled with the corresponding attribute values from the sample information file. "/>
-                  </AccessibilityProperties>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel8">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Default Track Height, Other (Pixels)" noResource="true"/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JTextField" name="defaultTrackHeightField">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="15" noResource="true"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="defaultTrackHeightFieldActionPerformed"/>
-                    <EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="defaultTrackHeightFieldFocusLost"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JLabel" name="missingDataExplanation4">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="Lucida Grande" size="12" style="2"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="&lt;html&gt;Default height of chart tracks (barcharts, scatterplots, etc)" noResource="true"/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JLabel" name="missingDataExplanation5">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="Lucida Grande" size="12" style="2"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="&lt;html&gt;Default height of all other tracks" noResource="true"/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel23">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Expand Feature Tracks" noResource="true"/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JLabel" name="missingDataExplanation3">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="Lucida Grande" size="12" style="2"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="&lt;html&gt;&lt;i&gt; If selected feature tracks are expanded by default." noResource="true"/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="expandCB">
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="expandCBActionPerformed"/>
-                  </Events>
-                </Component>
-              </SubComponents>
-            </Container>
-          </SubComponents>
-        </Container>
-        <Container class="javax.swing.JPanel" name="overlaysPanel">
-          <Constraints>
-            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
-              <JTabbedPaneConstraints tabName="Overlays">
-                <Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-                  <PlainString value="Overlays"/>
-                </Property>
-              </JTabbedPaneConstraints>
-            </Constraint>
-          </Constraints>
-
-          <Layout>
-            <DimensionLayout dim="0">
-              <Group type="103" groupAlignment="0" attributes="0">
-                  <Group type="102" alignment="0" attributes="0">
-                      <EmptySpace min="-2" pref="28" max="-2" attributes="0"/>
-                      <Component id="jPanel5" min="-2" max="-2" attributes="0"/>
-                      <EmptySpace pref="196" max="32767" attributes="0"/>
-                  </Group>
-              </Group>
-            </DimensionLayout>
-            <DimensionLayout dim="1">
-              <Group type="103" groupAlignment="0" attributes="0">
-                  <Group type="102" alignment="0" attributes="0">
-                      <EmptySpace min="-2" pref="55" max="-2" attributes="0"/>
-                      <Component id="jPanel5" min="-2" max="-2" attributes="0"/>
-                      <EmptySpace pref="214" max="32767" attributes="0"/>
-                  </Group>
-              </Group>
-            </DimensionLayout>
-          </Layout>
-          <SubComponents>
-            <Container class="javax.swing.JPanel" name="jPanel5">
-
-              <Layout>
-                <DimensionLayout dim="0">
-                  <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" attributes="0">
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="0" attributes="0">
-                              <Component id="displayTracksCB" alignment="0" min="-2" max="-2" attributes="0"/>
-                              <Component id="jLabel4" alignment="0" min="-2" max="-2" attributes="0"/>
-                              <Component id="overlayTrackCB" alignment="0" min="-2" max="-2" attributes="0"/>
-                              <Component id="jLabel2" alignment="0" min="-2" max="-2" attributes="0"/>
-                              <Group type="102" alignment="0" attributes="0">
-                                  <EmptySpace min="-2" pref="59" max="-2" attributes="0"/>
-                                  <Group type="103" groupAlignment="0" attributes="0">
-                                      <Group type="102" alignment="0" attributes="0">
-                                          <EmptySpace max="-2" attributes="0"/>
-                                          <Component id="colorOverlyCB" min="-2" max="-2" attributes="0"/>
-                                          <EmptySpace max="-2" attributes="0"/>
-                                          <Component id="chooseOverlayColorsButton" min="-2" max="-2" attributes="0"/>
-                                      </Group>
-                                      <Group type="102" alignment="0" attributes="0">
-                                          <Component id="jLabel3" min="-2" max="-2" attributes="0"/>
-                                          <EmptySpace max="-2" attributes="0"/>
-                                          <Component id="overlayAttributeTextField" min="-2" pref="228" max="-2" attributes="0"/>
-                                      </Group>
-                                  </Group>
-                              </Group>
-                          </Group>
-                          <EmptySpace max="-2" attributes="0"/>
-                      </Group>
-                  </Group>
-                </DimensionLayout>
-                <DimensionLayout dim="1">
-                  <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" attributes="0">
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="jLabel2" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="jLabel4" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace min="-2" pref="39" max="-2" attributes="0"/>
-                          <Component id="overlayTrackCB" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="3" attributes="0">
-                              <Component id="jLabel3" alignment="3" min="-2" max="-2" attributes="0"/>
-                              <Component id="overlayAttributeTextField" alignment="3" min="-2" max="-2" attributes="0"/>
-                          </Group>
-                          <EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="1" attributes="0">
-                              <Component id="colorOverlyCB" min="-2" max="-2" attributes="0"/>
-                              <Component id="chooseOverlayColorsButton" min="-2" max="-2" attributes="0"/>
-                          </Group>
-                          <EmptySpace min="-2" pref="36" max="-2" attributes="0"/>
-                          <Component id="displayTracksCB" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace pref="30" max="32767" attributes="0"/>
-                      </Group>
-                  </Group>
-                </DimensionLayout>
-              </Layout>
-              <SubComponents>
-                <Component class="javax.swing.JLabel" name="jLabel3">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Overlay tracks based on attribute:" noResource="true"/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JTextField" name="overlayAttributeTextField">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="LINKING_ID" noResource="true"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="overlayAttributeTextFieldActionPerformed"/>
-                    <EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="overlayAttributeTextFieldFocusLost"/>
-                    <EventHandler event="keyTyped" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="overlayAttributeTextFieldKeyTyped"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="overlayTrackCB">
-                  <Properties>
-                    <Property name="selected" type="boolean" value="true"/>
-                    <Property name="text" type="java.lang.String" value="Overlay mutation tracks" noResource="true"/>
-                    <Property name="actionCommand" type="java.lang.String" value="overlayTracksCB" noResource="true"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="overlayTrackCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel2">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="Lucida Grande" size="12" style="2"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
-                      <Connection code="getOverlayText()" type="code"/>
-                    </Property>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="displayTracksCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Display mutation data as distinct tracks" noResource="true"/>
-                    <Property name="actionCommand" type="java.lang.String" value="displayTracksCB" noResource="true"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="displayTracksCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel4">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="Lucida Grande" size="12" style="2"/>
-                    </Property>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="colorOverlyCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Color code overlay" noResource="true"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="colorOverlyCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="com.jidesoft.swing.JideButton" name="chooseOverlayColorsButton">
-                  <Properties>
-                    <Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
-                      <Color blue="f7" green="0" red="0" type="rgb"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="Choose colors" noResource="true"/>
-                    <Property name="buttonStyle" type="int" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
-                      <Connection code="&#xa;com.jidesoft.swing.ButtonStyle.HYPERLINK_STYLE &#xa;" type="code"/>
-                    </Property>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="Lucida Grande" size="12" style="2"/>
-                    </Property>
-                    <Property name="verticalAlignment" type="int" value="3"/>
-                    <Property name="verticalTextPosition" type="int" value="3"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="chooseOverlayColorsButtonActionPerformed"/>
-                  </Events>
-                </Component>
-              </SubComponents>
-            </Container>
-          </SubComponents>
-        </Container>
-        <Container class="javax.swing.JPanel" name="chartPanel">
-          <Constraints>
-            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
-              <JTabbedPaneConstraints tabName="Charts">
-                <Property name="tabTitle" type="java.lang.String" value="Charts"/>
-              </JTabbedPaneConstraints>
-            </Constraint>
-          </Constraints>
-
-          <Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
-            <Property name="useNullLayout" type="boolean" value="true"/>
-          </Layout>
-          <SubComponents>
-            <Container class="javax.swing.JPanel" name="jPanel4">
-              <Constraints>
-                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout" value="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout$AbsoluteConstraintsDescription">
-                  <AbsoluteConstraints x="20" y="30" width="580" height="330"/>
-                </Constraint>
-              </Constraints>
-
-              <Layout>
-                <DimensionLayout dim="0">
-                  <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" attributes="0">
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="0" attributes="0">
-                              <Component id="label1" alignment="0" min="-2" max="-2" attributes="0"/>
-                              <Group type="102" alignment="0" attributes="0">
-                                  <EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
-                                  <Group type="103" groupAlignment="0" attributes="0">
-                                      <Group type="102" alignment="0" attributes="0">
-                                          <Component id="chartDrawTrackNameCB" min="-2" max="-2" attributes="0"/>
-                                          <EmptySpace min="-2" pref="61" max="-2" attributes="0"/>
-                                          <Component id="jLabel7" min="-2" max="-2" attributes="0"/>
-                                      </Group>
-                                      <Component id="topBorderCB" alignment="0" min="-2" max="-2" attributes="0"/>
-                                      <Component id="colorBordersCB" alignment="0" min="-2" max="-2" attributes="0"/>
-                                      <Component id="bottomBorderCB" alignment="0" min="-2" max="-2" attributes="0"/>
-                                      <Component id="labelYAxisCB" alignment="0" min="-2" max="-2" attributes="0"/>
-                                  </Group>
-                              </Group>
-                          </Group>
-                          <EmptySpace max="-2" attributes="0"/>
-                      </Group>
-                  </Group>
-                </DimensionLayout>
-                <DimensionLayout dim="1">
-                  <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" attributes="0">
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="label1" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="topBorderCB" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="bottomBorderCB" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace min="-2" pref="7" max="-2" attributes="0"/>
-                          <Component id="colorBordersCB" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="0" attributes="0">
-                              <Component id="jLabel7" min="-2" max="-2" attributes="0"/>
-                              <Component id="chartDrawTrackNameCB" min="-2" max="-2" attributes="0"/>
-                          </Group>
-                          <EmptySpace min="-2" pref="32" max="-2" attributes="0"/>
-                          <Component id="labelYAxisCB" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace min="-2" pref="122" max="-2" attributes="0"/>
-                      </Group>
-                  </Group>
-                </DimensionLayout>
-              </Layout>
-              <SubComponents>
-                <Component class="javax.swing.JCheckBox" name="topBorderCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Draw Top Border"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="topBorderCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="java.awt.Label" name="label1">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
-                      <FontInfo relative="true">
-                        <Font component="label1" property="font" relativeSize="true" size="0"/>
-                      </FontInfo>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="Settings for barcharts and scatterplots:"/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="chartDrawTrackNameCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Draw Track Label"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="chartDrawTrackNameCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="bottomBorderCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Draw Bottom Border"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bottomBorderCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel7">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="&lt;html&gt;&lt;i&gt;Draw a label centered over the track provided&lt;br&gt;the track height is at least 25 pixels. "/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="colorBordersCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Color Borders"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="colorBordersCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="labelYAxisCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Label Y Axis"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="labelYAxisCBActionPerformed"/>
-                  </Events>
-                </Component>
-              </SubComponents>
-            </Container>
-          </SubComponents>
-        </Container>
-        <Container class="javax.swing.JPanel" name="alignmentPanel">
-          <Constraints>
-            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
-              <JTabbedPaneConstraints tabName="Alignments">
-                <Property name="tabTitle" type="java.lang.String" value="Alignments"/>
-              </JTabbedPaneConstraints>
-            </Constraint>
-          </Constraints>
-
-          <Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
-            <Property name="useNullLayout" type="boolean" value="true"/>
-          </Layout>
-          <SubComponents>
-            <Container class="javax.swing.JPanel" name="jPanel1">
-              <Constraints>
-                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout" value="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout$AbsoluteConstraintsDescription">
-                  <AbsoluteConstraints x="20" y="0" width="740" height="510"/>
-                </Constraint>
-              </Constraints>
-
-              <Layout>
-                <DimensionLayout dim="0">
-                  <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" alignment="0" attributes="0">
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="0" attributes="0">
-                              <Group type="102" alignment="0" attributes="0">
-                                  <Component id="legendPanel" min="-2" max="-2" attributes="0"/>
-                                  <EmptySpace max="-2" attributes="0"/>
-                              </Group>
-                              <Group type="103" alignment="0" groupAlignment="0" attributes="0">
-                                  <Group type="102" alignment="0" attributes="0">
-                                      <Component id="samShadeMismatchedBaseCB" min="-2" pref="290" max="-2" attributes="0"/>
-                                      <EmptySpace min="-2" pref="32" max="-2" attributes="0"/>
-                                      <Component id="jLabel19" min="-2" max="-2" attributes="0"/>
-                                      <EmptySpace type="unrelated" max="-2" attributes="0"/>
-                                      <Component id="samMinBaseQualityField" min="-2" pref="50" max="-2" attributes="0"/>
-                                      <EmptySpace min="-2" pref="31" max="-2" attributes="0"/>
-                                      <Component id="jLabel20" min="-2" max="-2" attributes="0"/>
-                                      <EmptySpace max="-2" attributes="0"/>
-                                      <Component id="samMaxBaseQualityField" min="-2" pref="50" max="-2" attributes="0"/>
-                                      <EmptySpace max="-2" attributes="0"/>
-                                  </Group>
-                                  <Group type="103" alignment="0" groupAlignment="0" attributes="0">
-                                      <Group type="102" alignment="0" attributes="0">
-                                          <Component id="showCovTrackCB" min="-2" pref="270" max="-2" attributes="0"/>
-                                          <EmptySpace max="-2" attributes="0"/>
-                                      </Group>
-                                      <Group type="103" alignment="0" groupAlignment="0" attributes="0">
-                                          <Group type="102" attributes="0">
-                                              <Group type="103" groupAlignment="0" attributes="0">
-                                                  <Group type="102" alignment="0" attributes="0">
-                                                      <Component id="jLabel11" min="-2" max="-2" attributes="0"/>
-                                                      <EmptySpace min="14" pref="14" max="14" attributes="0"/>
-                                                      <Component id="samMaxWindowSizeField" min="-2" max="-2" attributes="0"/>
-                                                      <EmptySpace min="16" pref="16" max="16" attributes="0"/>
-                                                      <Component id="jLabel12" min="-2" max="-2" attributes="0"/>
-                                                  </Group>
-                                                  <Component id="jLabel10" alignment="0" min="-2" pref="640" max="-2" attributes="0"/>
-                                                  <Group type="102" alignment="0" attributes="0">
-                                                      <Component id="jLabel13" min="-2" max="-2" attributes="0"/>
-                                                      <EmptySpace min="67" pref="67" max="67" attributes="0"/>
-                                                      <Component id="samMaxLevelField" min="-2" max="-2" attributes="0"/>
-                                                      <EmptySpace min="16" pref="16" max="16" attributes="0"/>
-                                                      <Component id="jLabel14" min="-2" pref="390" max="-2" attributes="0"/>
-                                                  </Group>
-                                                  <Group type="102" alignment="0" attributes="0">
-                                                      <Group type="103" groupAlignment="0" attributes="0">
-                                                          <Group type="102" alignment="0" attributes="0">
-                                                              <Component id="jLabel15" min="-2" max="-2" attributes="0"/>
-                                                              <EmptySpace min="31" pref="31" max="31" attributes="0"/>
-                                                              <Component id="mappingQualityThresholdField" min="-2" pref="70" max="-2" attributes="0"/>
-                                                          </Group>
-                                                          <Group type="102" alignment="0" attributes="0">
-                                                              <Component id="jLabel17" min="-2" pref="200" max="-2" attributes="0"/>
-                                                              <Component id="insertSizeThresholdField" min="-2" pref="70" max="-2" attributes="0"/>
-                                                          </Group>
-                                                      </Group>
-                                                      <EmptySpace min="-2" pref="30" max="-2" attributes="0"/>
-                                                      <Group type="103" groupAlignment="0" attributes="0">
-                                                          <Component id="jLabel18" alignment="0" min="-2" pref="360" max="-2" attributes="0"/>
-                                                          <Component id="jLabel16" alignment="0" min="-2" max="-2" attributes="0"/>
-                                                      </Group>
-                                                  </Group>
-                                              </Group>
-                                              <EmptySpace pref="30" max="32767" attributes="0"/>
-                                          </Group>
-                                          <Group type="102" alignment="0" attributes="0">
-                                              <Group type="103" groupAlignment="1" attributes="0">
-                                                  <Group type="102" attributes="0">
-                                                      <Component id="samShowDuplicatesCB" min="-2" pref="290" max="-2" attributes="0"/>
-                                                      <EmptySpace pref="20" max="32767" attributes="0"/>
-                                                  </Group>
-                                                  <Group type="102" alignment="1" attributes="0">
-                                                      <Component id="samFlagUnmappedPairCB" min="-2" pref="310" max="-2" attributes="0"/>
-                                                      <EmptySpace max="-2" attributes="0"/>
-                                                  </Group>
-                                              </Group>
-                                              <Group type="103" groupAlignment="0" attributes="0">
-                                                  <Component id="showRefSeqCB" alignment="0" min="-2" pref="270" max="-2" attributes="0"/>
-                                                  <Component id="shadeCenterCB" alignment="0" min="-2" pref="450" max="-2" attributes="0"/>
-                                              </Group>
-                                              <EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
-                                          </Group>
-                                          <Group type="102" alignment="0" attributes="0">
-                                              <Component id="filterCB" min="-2" pref="144" max="-2" attributes="0"/>
-                                              <EmptySpace min="-2" pref="27" max="-2" attributes="0"/>
-                                              <Component id="filterURL" min="-2" pref="482" max="-2" attributes="0"/>
-                                              <EmptySpace max="-2" attributes="0"/>
-                                          </Group>
-                                      </Group>
-                                  </Group>
-                              </Group>
-                          </Group>
-                      </Group>
-                  </Group>
-                </DimensionLayout>
-                <DimensionLayout dim="1">
-                  <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" attributes="0">
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="2" attributes="0">
-                              <Component id="jLabel11" alignment="2" min="-2" max="-2" attributes="0"/>
-                              <Component id="samMaxWindowSizeField" alignment="2" min="-2" max="-2" attributes="0"/>
-                              <Component id="jLabel12" alignment="2" min="-2" max="-2" attributes="0"/>
-                          </Group>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="2" attributes="0">
-                              <Component id="jLabel13" alignment="2" min="-2" max="-2" attributes="0"/>
-                              <Component id="samMaxLevelField" alignment="2" min="-2" max="-2" attributes="0"/>
-                              <Component id="jLabel14" alignment="2" min="-2" pref="30" max="-2" attributes="0"/>
-                          </Group>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="2" attributes="0">
-                              <Component id="jLabel15" alignment="2" min="-2" max="-2" attributes="0"/>
-                              <Component id="mappingQualityThresholdField" alignment="2" min="-2" max="-2" attributes="0"/>
-                              <Component id="jLabel16" alignment="2" min="-2" max="-2" attributes="0"/>
-                          </Group>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="2" attributes="0">
-                              <Component id="jLabel17" alignment="2" min="-2" max="-2" attributes="0"/>
-                              <Component id="insertSizeThresholdField" alignment="2" min="-2" max="-2" attributes="0"/>
-                              <Component id="jLabel18" alignment="2" min="-2" pref="40" max="-2" attributes="0"/>
-                          </Group>
-                          <EmptySpace type="separate" max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="1" attributes="0">
-                              <Group type="102" alignment="1" attributes="0">
-                                  <Component id="samShowDuplicatesCB" min="-2" max="-2" attributes="0"/>
-                                  <EmptySpace min="-2" pref="30" max="-2" attributes="0"/>
-                              </Group>
-                              <Group type="102" alignment="1" attributes="0">
-                                  <Component id="showRefSeqCB" min="-2" max="-2" attributes="0"/>
-                                  <EmptySpace min="7" pref="7" max="7" attributes="0"/>
-                                  <Group type="103" groupAlignment="3" attributes="0">
-                                      <Component id="shadeCenterCB" alignment="3" min="-2" max="-2" attributes="0"/>
-                                      <Component id="samFlagUnmappedPairCB" alignment="3" min="-2" max="-2" attributes="0"/>
-                                  </Group>
-                              </Group>
-                          </Group>
-                          <EmptySpace type="unrelated" max="-2" attributes="0"/>
-                          <Component id="showCovTrackCB" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace type="unrelated" max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="2" attributes="0">
-                              <Component id="samShadeMismatchedBaseCB" alignment="2" min="-2" max="-2" attributes="0"/>
-                              <Component id="samMinBaseQualityField" alignment="2" min="-2" max="-2" attributes="0"/>
-                              <Component id="samMaxBaseQualityField" alignment="2" min="-2" max="-2" attributes="0"/>
-                              <Component id="jLabel19" alignment="2" min="-2" max="-2" attributes="0"/>
-                              <Component id="jLabel20" alignment="2" min="-2" max="-2" attributes="0"/>
-                          </Group>
-                          <EmptySpace min="-2" pref="22" max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="3" attributes="0">
-                              <Component id="filterCB" alignment="3" min="-2" max="-2" attributes="0"/>
-                              <Component id="filterURL" alignment="3" min="-2" max="-2" attributes="0"/>
-                          </Group>
-                          <EmptySpace min="-2" pref="29" max="-2" attributes="0"/>
-                          <Component id="jLabel10" min="-2" pref="30" max="-2" attributes="0"/>
-                          <EmptySpace type="unrelated" max="-2" attributes="0"/>
-                          <Component id="legendPanel" max="32767" attributes="0"/>
-                          <EmptySpace max="-2" attributes="0"/>
-                      </Group>
-                  </Group>
-                </DimensionLayout>
-              </Layout>
-              <SubComponents>
-                <Component class="javax.swing.JTextField" name="samMaxWindowSizeField">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="jTextField1"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="samMaxWindowSizeFieldActionPerformed"/>
-                    <EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="samMaxWindowSizeFieldFocusLost"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel11">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Visibility range threshold (kb)"/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="samShowDuplicatesCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Show duplicate reads"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="samShowDuplicatesCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel12">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="&lt;html&gt;&lt;i&gt;Nominal window size at which alignments become visible"/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="samFlagUnmappedPairCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Flag unmapped pairs"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="samFlagUnmappedPairCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel13">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Maximum read depth"/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JTextField" name="samMaxLevelField">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="jTextField1"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="samMaxLevelFieldActionPerformed"/>
-                    <EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="samMaxLevelFieldFocusLost"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel14">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="&lt;html&gt;&lt;i&gt;Maximum number of rows of alignments to display."/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel15">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Mapping quality threshold:"/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JTextField" name="mappingQualityThresholdField">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="0"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="mappingQualityThresholdFieldActionPerformed"/>
-                    <EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="mappingQualityThresholdFieldFocusLost"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel16">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="&lt;html&gt;&lt;i&gt;Reads with qualities  below the threshold are not shown."/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel17">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Insert size flagging threshold:"/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JTextField" name="insertSizeThresholdField">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="0"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="insertSizeThresholdFieldActionPerformed"/>
-                    <EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="insertSizeThresholdFieldFocusLost"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel18">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="&lt;html&gt;&lt;i&gt;Paired end alignments with insert sizes &gt; this value are flagged."/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel10">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="&lt;html&gt;Chromosome color legend &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;i&gt;Used to flag paired end reads with mates on other chromosomes"/>
-                  </Properties>
-                </Component>
-                <Container class="javax.swing.JPanel" name="legendPanel">
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new ChromosomeColorLegend();"/>
-                  </AuxValues>
-
-                  <Layout>
-                    <DimensionLayout dim="0">
-                      <Group type="103" groupAlignment="0" attributes="0">
-                          <EmptySpace min="0" pref="723" max="32767" attributes="0"/>
-                      </Group>
-                    </DimensionLayout>
-                    <DimensionLayout dim="1">
-                      <Group type="103" groupAlignment="0" attributes="0">
-                          <EmptySpace min="0" pref="57" max="32767" attributes="0"/>
-                      </Group>
-                    </DimensionLayout>
-                  </Layout>
-                </Container>
-                <Component class="javax.swing.JCheckBox" name="showRefSeqCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Show reference sequence"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showRefSeqCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="shadeCenterCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Shade alignments intersecting center"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="shadeCenterCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="showCovTrackCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Show coverage track"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showCovTrackCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="samShadeMismatchedBaseCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Shade mismatched bases by quality. "/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="samShadeMismatchedBaseCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JTextField" name="samMinBaseQualityField">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="0"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="samMinBaseQualityFieldActionPerformed"/>
-                    <EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="samMinBaseQualityFieldFocusLost"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JTextField" name="samMaxBaseQualityField">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="0"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="samMaxBaseQualityFieldActionPerformed"/>
-                    <EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="samMaxBaseQualityFieldFocusLost"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel19">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Min: "/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel20">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Max:"/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="filterCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Filter alignments"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="filterCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JTextField" name="filterURL">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="URL or path to filter file"/>
-                    <Property name="enabled" type="boolean" value="false"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="filterURLActionPerformed"/>
-                    <EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="filterURLFocusLost"/>
-                  </Events>
-                </Component>
-              </SubComponents>
-            </Container>
-          </SubComponents>
-        </Container>
-        <Container class="javax.swing.JPanel" name="expressionPane">
-          <Constraints>
-            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
-              <JTabbedPaneConstraints tabName="Probes">
-                <Property name="tabTitle" type="java.lang.String" value="Probes"/>
-              </JTabbedPaneConstraints>
-            </Constraint>
-          </Constraints>
-
-          <Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
-            <Property name="useNullLayout" type="boolean" value="true"/>
-          </Layout>
-          <SubComponents>
-            <Container class="javax.swing.JPanel" name="jPanel8">
-              <Constraints>
-                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout" value="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout$AbsoluteConstraintsDescription">
-                  <AbsoluteConstraints x="10" y="30" width="720" height="310"/>
-                </Constraint>
-              </Constraints>
-
-              <Layout>
-                <DimensionLayout dim="0">
-                  <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" attributes="0">
-                          <Group type="103" groupAlignment="0" attributes="0">
-                              <Group type="102" alignment="0" attributes="0">
-                                  <EmptySpace min="-2" pref="45" max="-2" attributes="0"/>
-                                  <Group type="103" groupAlignment="0" attributes="0">
-                                      <Component id="expMapToLociCB" alignment="0" min="-2" max="-2" attributes="0"/>
-                                      <Component id="expMapToGeneCB" alignment="0" min="-2" max="-2" attributes="0"/>
-                                  </Group>
-                              </Group>
-                              <Group type="102" alignment="0" attributes="0">
-                                  <EmptySpace max="-2" attributes="0"/>
-                                  <Group type="103" groupAlignment="0" attributes="0">
-                                      <Group type="102" attributes="0">
-                                          <EmptySpace min="24" pref="24" max="24" attributes="0"/>
-                                          <Component id="jLabel21" min="-2" pref="497" max="-2" attributes="0"/>
-                                      </Group>
-                                      <Component id="jLabel24" alignment="0" min="-2" max="-2" attributes="0"/>
-                                  </Group>
-                              </Group>
-                          </Group>
-                          <EmptySpace pref="179" max="32767" attributes="0"/>
-                      </Group>
-                  </Group>
-                </DimensionLayout>
-                <DimensionLayout dim="1">
-                  <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" alignment="0" attributes="0">
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="jLabel24" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="jLabel21" min="-2" pref="44" max="-2" attributes="0"/>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="expMapToLociCB" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace min="-2" pref="14" max="-2" attributes="0"/>
-                          <Component id="expMapToGeneCB" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace pref="158" max="32767" attributes="0"/>
-                      </Group>
-                  </Group>
-                </DimensionLayout>
-              </Layout>
-              <SubComponents>
-                <Component class="javax.swing.JRadioButton" name="expMapToGeneCB">
-                  <Properties>
-                    <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
-                      <ComponentRef name="probeMappingButtonGroup"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="Map probes to genes"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="expMapToGeneCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel24">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Expression probe mapping options: "/>
-                  </Properties>
-                </Component>
-                <Component class="javax.swing.JRadioButton" name="expMapToLociCB">
-                  <Properties>
-                    <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
-                      <ComponentRef name="probeMappingButtonGroup"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="&lt;html&gt;Map probes to target loci"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="expMapToLociCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel21">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="&lt;html&gt;&lt;i&gt;Note: Changes will not affect currently loaded datasets."/>
-                  </Properties>
-                </Component>
-              </SubComponents>
-            </Container>
-          </SubComponents>
-        </Container>
-        <Container class="javax.swing.JPanel" name="advancedPanel">
-          <Properties>
-            <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
-              <Border info="org.netbeans.modules.form.compat2.border.EmptyBorderInfo">
-                <EmptyBorder bottom="1" left="10" right="10" top="1"/>
-              </Border>
-            </Property>
-          </Properties>
-          <Constraints>
-            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
-              <JTabbedPaneConstraints tabName="Advanced">
-                <Property name="tabTitle" type="java.lang.String" value="Advanced"/>
-              </JTabbedPaneConstraints>
-            </Constraint>
-          </Constraints>
-
-          <Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
-            <Property name="useNullLayout" type="boolean" value="true"/>
-          </Layout>
-          <SubComponents>
-            <Container class="javax.swing.JPanel" name="jPanel3">
-              <Constraints>
-                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout" value="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout$AbsoluteConstraintsDescription">
-                  <AbsoluteConstraints x="10" y="0" width="750" height="350"/>
-                </Constraint>
-              </Constraints>
-
-              <Layout>
-                <DimensionLayout dim="0">
-                  <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" attributes="0">
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="0" attributes="0">
-                              <Group type="102" alignment="1" attributes="0">
-                                  <Component id="jPanel2" min="-2" max="-2" attributes="0"/>
-                                  <EmptySpace min="-2" pref="14" max="-2" attributes="0"/>
-                              </Group>
-                              <Group type="102" alignment="0" attributes="0">
-                                  <Component id="jPanel7" max="32767" attributes="0"/>
-                                  <EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
-                              </Group>
-                          </Group>
-                      </Group>
-                  </Group>
-                </DimensionLayout>
-                <DimensionLayout dim="1">
-                  <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" alignment="0" attributes="0">
-                          <EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
-                          <Component id="jPanel7" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace type="unrelated" max="-2" attributes="0"/>
-                          <Component id="jPanel2" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace max="32767" attributes="0"/>
-                      </Group>
-                  </Group>
-                </DimensionLayout>
-              </Layout>
-              <SubComponents>
-                <Container class="javax.swing.JPanel" name="jPanel2">
-
-                  <Layout>
-                    <DimensionLayout dim="0">
-                      <Group type="103" groupAlignment="0" attributes="0">
-                          <Group type="102" attributes="0">
-                              <EmptySpace max="-2" attributes="0"/>
-                              <Group type="103" groupAlignment="0" attributes="0">
-                                  <Group type="102" alignment="0" attributes="0">
-                                      <EmptySpace min="-2" pref="29" max="-2" attributes="0"/>
-                                      <Group type="103" groupAlignment="0" attributes="0">
-                                          <Component id="jLabel1" alignment="0" min="-2" max="-2" attributes="0"/>
-                                          <Group type="102" alignment="0" attributes="0">
-                                              <Component id="jLabel6" min="-2" max="-2" attributes="0"/>
-                                              <EmptySpace min="-2" pref="44" max="-2" attributes="0"/>
-                                              <Group type="103" groupAlignment="0" max="-2" attributes="0">
-                                                  <Component id="dataServerURLTextField" max="32767" attributes="1"/>
-                                                  <Component id="genomeServerURLTextField" alignment="0" min="-2" pref="494" max="-2" attributes="1"/>
-                                              </Group>
-                                          </Group>
-                                      </Group>
-                                  </Group>
-                                  <Group type="102" alignment="0" attributes="0">
-                                      <Component id="editServerPropertiesCB" min="-2" max="-2" attributes="0"/>
-                                      <EmptySpace type="separate" max="-2" attributes="0"/>
-                                      <Component id="jButton1" min="-2" max="-2" attributes="0"/>
-                                  </Group>
-                                  <Component id="clearGenomeCacheButton" alignment="0" min="-2" max="-2" attributes="0"/>
-                              </Group>
-                              <EmptySpace max="32767" attributes="0"/>
-                          </Group>
-                      </Group>
-                    </DimensionLayout>
-                    <DimensionLayout dim="1">
-                      <Group type="103" groupAlignment="0" attributes="0">
-                          <Group type="102" attributes="0">
-                              <EmptySpace max="-2" attributes="0"/>
-                              <Group type="103" groupAlignment="3" attributes="0">
-                                  <Component id="editServerPropertiesCB" alignment="3" min="-2" max="-2" attributes="0"/>
-                                  <Component id="jButton1" alignment="3" min="-2" max="-2" attributes="0"/>
-                              </Group>
-                              <EmptySpace max="-2" attributes="0"/>
-                              <Group type="103" groupAlignment="2" attributes="0">
-                                  <Component id="jLabel1" alignment="2" min="-2" max="-2" attributes="0"/>
-                                  <Component id="genomeServerURLTextField" alignment="2" min="-2" max="-2" attributes="0"/>
-                              </Group>
-                              <EmptySpace max="-2" attributes="0"/>
-                              <Group type="103" groupAlignment="3" attributes="0">
-                                  <Component id="jLabel6" alignment="3" min="-2" max="-2" attributes="0"/>
-                                  <Component id="dataServerURLTextField" alignment="3" min="-2" max="-2" attributes="0"/>
-                              </Group>
-                              <EmptySpace type="separate" max="-2" attributes="0"/>
-                              <Component id="clearGenomeCacheButton" min="-2" max="-2" attributes="0"/>
-                              <EmptySpace max="32767" attributes="0"/>
-                          </Group>
-                      </Group>
-                    </DimensionLayout>
-                  </Layout>
-                  <SubComponents>
-                    <Component class="javax.swing.JLabel" name="jLabel1">
-                      <Properties>
-                        <Property name="text" type="java.lang.String" value="Genome Server URL"/>
-                      </Properties>
-                    </Component>
-                    <Component class="javax.swing.JTextField" name="genomeServerURLTextField">
-                      <Properties>
-                        <Property name="text" type="java.lang.String" value="jTextField1"/>
-                        <Property name="enabled" type="boolean" value="false"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="genomeServerURLTextFieldActionPerformed"/>
-                        <EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="genomeServerURLTextFieldFocusLost"/>
-                      </Events>
-                    </Component>
-                    <Component class="javax.swing.JLabel" name="jLabel6">
-                      <Properties>
-                        <Property name="text" type="java.lang.String" value="Data Registry URL"/>
-                      </Properties>
-                    </Component>
-                    <Component class="javax.swing.JTextField" name="dataServerURLTextField">
-                      <Properties>
-                        <Property name="enabled" type="boolean" value="false"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="dataServerURLTextFieldActionPerformed"/>
-                        <EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="dataServerURLTextFieldFocusLost"/>
-                      </Events>
-                    </Component>
-                    <Component class="javax.swing.JCheckBox" name="editServerPropertiesCB">
-                      <Properties>
-                        <Property name="text" type="java.lang.String" value="Edit server properties"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="editServerPropertiesCBActionPerformed"/>
-                      </Events>
-                    </Component>
-                    <Component class="javax.swing.JButton" name="clearGenomeCacheButton">
-                      <Properties>
-                        <Property name="text" type="java.lang.String" value="Clear Genome Cache"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="clearGenomeCacheButtonActionPerformed"/>
-                      </Events>
-                    </Component>
-                    <Component class="javax.swing.JButton" name="jButton1">
-                      <Properties>
-                        <Property name="text" type="java.lang.String" value="Reset to Defaults"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton1ActionPerformed"/>
-                      </Events>
-                    </Component>
-                  </SubComponents>
-                </Container>
-                <Container class="javax.swing.JPanel" name="jPanel7">
-
-                  <Layout>
-                    <DimensionLayout dim="0">
-                      <Group type="103" groupAlignment="0" attributes="0">
-                          <Group type="102" attributes="0">
-                              <Group type="103" groupAlignment="0" attributes="0">
-                                  <Group type="102" alignment="0" attributes="0">
-                                      <EmptySpace max="-2" attributes="0"/>
-                                      <Component id="enablePortCB" min="-2" max="-2" attributes="0"/>
-                                      <EmptySpace min="-2" pref="39" max="-2" attributes="0"/>
-                                      <Component id="portField" min="-2" pref="126" max="-2" attributes="0"/>
-                                  </Group>
-                                  <Group type="102" alignment="0" attributes="0">
-                                      <EmptySpace min="-2" pref="48" max="-2" attributes="0"/>
-                                      <Component id="jLabel22" min="-2" max="-2" attributes="0"/>
-                                  </Group>
-                              </Group>
-                              <EmptySpace pref="314" max="32767" attributes="0"/>
-                          </Group>
-                      </Group>
-                    </DimensionLayout>
-                    <DimensionLayout dim="1">
-                      <Group type="103" groupAlignment="0" attributes="0">
-                          <Group type="102" alignment="0" attributes="0">
-                              <EmptySpace min="-2" pref="28" max="-2" attributes="0"/>
-                              <Group type="103" groupAlignment="2" attributes="0">
-                                  <Component id="enablePortCB" alignment="2" min="-2" max="-2" attributes="0"/>
-                                  <Component id="portField" alignment="2" min="-2" max="-2" attributes="0"/>
-                              </Group>
-                              <EmptySpace type="unrelated" max="-2" attributes="0"/>
-                              <Component id="jLabel22" min="-2" max="-2" attributes="0"/>
-                              <EmptySpace pref="48" max="32767" attributes="0"/>
-                          </Group>
-                      </Group>
-                    </DimensionLayout>
-                  </Layout>
-                  <SubComponents>
-                    <Component class="javax.swing.JCheckBox" name="enablePortCB">
-                      <Properties>
-                        <Property name="text" type="java.lang.String" value="Enable port"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="enablePortCBActionPerformed"/>
-                      </Events>
-                    </Component>
-                    <Component class="javax.swing.JTextField" name="portField">
-                      <Properties>
-                        <Property name="text" type="java.lang.String" value="60151"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="portFieldActionPerformed"/>
-                        <EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="portFieldFocusLost"/>
-                      </Events>
-                    </Component>
-                    <Component class="javax.swing.JLabel" name="jLabel22">
-                      <Properties>
-                        <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                          <Font name="Lucida Grande" size="13" style="2"/>
-                        </Property>
-                        <Property name="text" type="java.lang.String" value="Enable port to send commands and http requests to IGV. "/>
-                      </Properties>
-                    </Component>
-                  </SubComponents>
-                </Container>
-              </SubComponents>
-            </Container>
-            <Container class="javax.swing.JPanel" name="jPanel9">
-              <Constraints>
-                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout" value="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout$AbsoluteConstraintsDescription">
-                  <AbsoluteConstraints x="30" y="370" width="710" height="120"/>
-                </Constraint>
-              </Constraints>
-
-              <Layout>
-                <DimensionLayout dim="0">
-                  <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" attributes="0">
-                          <Group type="103" groupAlignment="0" attributes="0">
-                              <Group type="102" alignment="0" attributes="0">
-                                  <EmptySpace min="-2" pref="44" max="-2" attributes="0"/>
-                                  <Component id="jLabel25" min="-2" pref="601" max="-2" attributes="0"/>
-                              </Group>
-                              <Group type="102" alignment="0" attributes="0">
-                                  <EmptySpace max="-2" attributes="0"/>
-                                  <Component id="useByteRangeCB" min="-2" max="-2" attributes="0"/>
-                              </Group>
-                          </Group>
-                          <EmptySpace pref="65" max="32767" attributes="0"/>
-                      </Group>
-                  </Group>
-                </DimensionLayout>
-                <DimensionLayout dim="1">
-                  <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" alignment="0" attributes="0">
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="useByteRangeCB" min="-2" pref="38" max="-2" attributes="0"/>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="jLabel25" pref="61" max="32767" attributes="0"/>
-                          <EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
-                      </Group>
-                  </Group>
-                </DimensionLayout>
-              </Layout>
-              <SubComponents>
-                <Component class="javax.swing.JCheckBox" name="useByteRangeCB">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="Use http byte-range requests"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="useByteRangeCBActionPerformed"/>
-                  </Events>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel25">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="Lucida Grande" size="13" style="2"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="&lt;html&gt;This option applies to certain &quot;Load from Server...&quot; tracks hosted at the Broad.    Disable this option if you are unable to load the phastCons conservation track under the hg18 annotations."/>
-                  </Properties>
-                </Component>
-              </SubComponents>
-            </Container>
-          </SubComponents>
-        </Container>
-      </SubComponents>
-    </Container>
-    <Container class="com.jidesoft.dialog.ButtonPanel" name="okCancelButtonPanel">
-      <Constraints>
-        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
-          <BorderConstraints direction="South"/>
-        </Constraint>
-      </Constraints>
-      <SubComponents>
-        <Component class="javax.swing.JButton" name="okButton">
-          <Properties>
-            <Property name="text" type="java.lang.String" value="OK" noResource="true"/>
-          </Properties>
-          <Events>
-            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="okButtonActionPerformed"/>
-          </Events>
-        </Component>
-        <Component class="javax.swing.JButton" name="cancelButton">
-          <Properties>
-            <Property name="text" type="java.lang.String" value="Cancel" noResource="true"/>
-          </Properties>
-          <Events>
-            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/>
-          </Events>
-        </Component>
-      </SubComponents>
-    </Container>
-  </SubComponents>
-</Form>
diff --git a/src/org/broad/igv/ui/PreferencesEditor.java b/src/org/broad/igv/ui/PreferencesEditor.java
index 82bf0eb..acba82a 100644
--- a/src/org/broad/igv/ui/PreferencesEditor.java
+++ b/src/org/broad/igv/ui/PreferencesEditor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -17,20 +17,30 @@
  */
 package org.broad.igv.ui;
 
-import org.broad.igv.IGVConstants;
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.border.*;
+
+import com.jidesoft.dialog.*;
+import com.jidesoft.swing.*;
 import org.broad.igv.PreferenceManager;
 import org.broad.igv.feature.GenomeManager;
 import org.broad.igv.feature.ProbeToGeneMap;
 import org.broad.igv.main.CommandListener;
-import org.broad.igv.track.TrackManager;
 import org.broad.igv.ui.legend.ChromosomeColorLegend;
 import org.broad.igv.ui.legend.LegendDialog;
 import org.broad.igv.ui.util.MessageUtils;
+import org.broad.igv.util.IGVHttpUtils;
+import org.broad.igv.util.Utilities;
 
 import javax.swing.*;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.jdesktop.layout.GroupLayout;
+import org.jdesktop.layout.LayoutStyle;
+
 /**
  * @author jrobinso
  */
@@ -38,17 +48,28 @@ public class PreferencesEditor extends javax.swing.JDialog {
 
     private boolean canceled = false;
     Map<String, String> updatedPreferenceMap = new HashMap();
-    PreferenceManager preferenceManager = PreferenceManager.getInstance();
+    PreferenceManager prefMgr = PreferenceManager.getInstance();
     boolean updateOverlays = false;
     boolean inputValidated = true;
-    int initialSamMaxLevel = 0;
-    int initialSamQualityThreshold = 0;
-    boolean initialShowDuplicates = false;
     private static int lastSelectedIndex = 0;
+    boolean proxySettingsChanged;
+
+
+    private void normalizeCoverageCBFocusLost(FocusEvent e) {
+        // TODO add your code here
+    }
+
+    private void clearAllProxyButtonActionPerformed(ActionEvent e) {
+        if (MessageUtils.confirm("This will immediately clear all proxy settings.  Are you sure?")) {
+            this.proxyHostField.setText("");
+            this.proxyPortField.setText("");
+            this.proxyUsernameField.setText("");
+            this.proxyPasswordField.setText("");
+            this.useProxyCB.setSelected(false);
+            PreferenceManager.getInstance().clearProxySettings();
+        }
+    }
 
-    /**
-     * Creates new form PreferencesEditor
-     */
     public PreferencesEditor(java.awt.Frame parent, boolean modal) {
         super(parent, modal);
         initComponents();
@@ -65,1165 +86,1738 @@ public class PreferencesEditor extends javax.swing.JDialog {
      * always regenerated by the Form Editor.
      */
     // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    // Generated using JFormDesigner non-commercial license
     private void initComponents() {
-
-        featureStyleButtonGroup = new javax.swing.ButtonGroup();
-        probeMappingButtonGroup = new javax.swing.ButtonGroup();
-        rnaiMapapingButtonGroup = new javax.swing.ButtonGroup();
-        tabbedPane = new javax.swing.JTabbedPane();
-        generalPanel = new javax.swing.JPanel();
-        jPanel10 = new javax.swing.JPanel();
-        missingDataExplanation = new javax.swing.JLabel();
-        showMissingDataCB = new javax.swing.JCheckBox();
-        combinePanelsCB = new javax.swing.JCheckBox();
-        joinSegmentsCB = new javax.swing.JCheckBox();
-        showAttributesDisplayCheckBox = new javax.swing.JCheckBox();
-        searchZoomCB = new javax.swing.JCheckBox();
-        missingDataExplanation6 = new javax.swing.JLabel();
-        missingDataExplanation7 = new javax.swing.JLabel();
-        tracksPanel = new javax.swing.JPanel();
-        jPanel6 = new javax.swing.JPanel();
-        jLabel5 = new javax.swing.JLabel();
-        defaultChartTrackHeightField = new javax.swing.JTextField();
-        trackNameAttributeLabel = new javax.swing.JLabel();
-        trackNameAttributeField = new javax.swing.JTextField();
-        missingDataExplanation2 = new javax.swing.JLabel();
-        jLabel8 = new javax.swing.JLabel();
-        defaultTrackHeightField = new javax.swing.JTextField();
-        missingDataExplanation4 = new javax.swing.JLabel();
-        missingDataExplanation5 = new javax.swing.JLabel();
-        jLabel23 = new javax.swing.JLabel();
-        missingDataExplanation3 = new javax.swing.JLabel();
-        expandCB = new javax.swing.JCheckBox();
-        overlaysPanel = new javax.swing.JPanel();
-        jPanel5 = new javax.swing.JPanel();
-        jLabel3 = new javax.swing.JLabel();
-        overlayAttributeTextField = new javax.swing.JTextField();
-        overlayTrackCB = new javax.swing.JCheckBox();
-        jLabel2 = new javax.swing.JLabel();
-        displayTracksCB = new javax.swing.JCheckBox();
-        jLabel4 = new javax.swing.JLabel();
-        colorOverlyCB = new javax.swing.JCheckBox();
-        chooseOverlayColorsButton = new com.jidesoft.swing.JideButton();
-        chartPanel = new javax.swing.JPanel();
-        jPanel4 = new javax.swing.JPanel();
-        topBorderCB = new javax.swing.JCheckBox();
-        label1 = new java.awt.Label();
-        chartDrawTrackNameCB = new javax.swing.JCheckBox();
-        bottomBorderCB = new javax.swing.JCheckBox();
-        jLabel7 = new javax.swing.JLabel();
-        colorBordersCB = new javax.swing.JCheckBox();
-        labelYAxisCB = new javax.swing.JCheckBox();
-        alignmentPanel = new javax.swing.JPanel();
-        jPanel1 = new javax.swing.JPanel();
-        samMaxWindowSizeField = new javax.swing.JTextField();
-        jLabel11 = new javax.swing.JLabel();
-        samShowDuplicatesCB = new javax.swing.JCheckBox();
-        jLabel12 = new javax.swing.JLabel();
-        samFlagUnmappedPairCB = new javax.swing.JCheckBox();
-        jLabel13 = new javax.swing.JLabel();
-        samMaxLevelField = new javax.swing.JTextField();
-        jLabel14 = new javax.swing.JLabel();
-        jLabel15 = new javax.swing.JLabel();
-        mappingQualityThresholdField = new javax.swing.JTextField();
-        jLabel16 = new javax.swing.JLabel();
-        jLabel17 = new javax.swing.JLabel();
-        insertSizeThresholdField = new javax.swing.JTextField();
-        jLabel18 = new javax.swing.JLabel();
-        jLabel10 = new javax.swing.JLabel();
+        tabbedPane = new JTabbedPane();
+        generalPanel = new JPanel();
+        jPanel10 = new JPanel();
+        missingDataExplanation = new JLabel();
+        showMissingDataCB = new JCheckBox();
+        combinePanelsCB = new JCheckBox();
+        joinSegmentsCB = new JCheckBox();
+        showAttributesDisplayCheckBox = new JCheckBox();
+        searchZoomCB = new JCheckBox();
+        missingDataExplanation6 = new JLabel();
+        missingDataExplanation7 = new JLabel();
+        tracksPanel = new JPanel();
+        jPanel6 = new JPanel();
+        jLabel5 = new JLabel();
+        defaultChartTrackHeightField = new JTextField();
+        trackNameAttributeLabel = new JLabel();
+        trackNameAttributeField = new JTextField();
+        missingDataExplanation2 = new JLabel();
+        jLabel8 = new JLabel();
+        defaultTrackHeightField = new JTextField();
+        missingDataExplanation4 = new JLabel();
+        missingDataExplanation5 = new JLabel();
+        missingDataExplanation3 = new JLabel();
+        expandCB = new JCheckBox();
+        normalizeCoverageCB = new JCheckBox();
+        missingDataExplanation8 = new JLabel();
+        expandIconCB = new JCheckBox();
+        overlaysPanel = new JPanel();
+        jPanel5 = new JPanel();
+        jLabel3 = new JLabel();
+        overlayAttributeTextField = new JTextField();
+        overlayTrackCB = new JCheckBox();
+        jLabel2 = new JLabel();
+        displayTracksCB = new JCheckBox();
+        jLabel4 = new JLabel();
+        colorOverlyCB = new JCheckBox();
+        chooseOverlayColorsButton = new JideButton();
+        chartPanel = new JPanel();
+        jPanel4 = new JPanel();
+        topBorderCB = new JCheckBox();
+        label1 = new Label();
+        chartDrawTrackNameCB = new JCheckBox();
+        bottomBorderCB = new JCheckBox();
+        jLabel7 = new JLabel();
+        colorBordersCB = new JCheckBox();
+        labelYAxisCB = new JCheckBox();
+        autoscaleCB = new JCheckBox();
+        jLabel9 = new JLabel();
+        showDatarangeCB = new JCheckBox();
+        alignmentPanel = new JPanel();
+        jPanel1 = new JPanel();
+        jPanel11 = new JPanel();
+        samMaxLevelsField = new JTextField();
+        snpThresholdField = new JTextField();
+        jLabel11 = new JLabel();
+        jLabel26 = new JLabel();
+        jLabel17 = new JLabel();
+        jLabel16 = new JLabel();
+        mappingQualityThresholdField = new JTextField();
+        jLabel14 = new JLabel();
+        insertSizeThresholdField = new JTextField();
+        jLabel13 = new JLabel();
+        jLabel15 = new JLabel();
+        jLabel18 = new JLabel();
+        samMaxWindowSizeField = new JTextField();
+        jLabel12 = new JLabel();
+        jPanel12 = new JPanel();
+        samMinBaseQualityField = new JTextField();
+        samShadeMismatchedBaseCB = new JCheckBox();
+        samMaxBaseQualityField = new JTextField();
+        showCovTrackCB = new JCheckBox();
+        samFilterDuplicatesCB = new JCheckBox();
+        showRefSeqCB = new JCheckBox();
+        jLabel19 = new JLabel();
+        filterCB = new JCheckBox();
+        filterURL = new JTextField();
+        samFlagUnmappedPairCB = new JCheckBox();
+        shadeCenterCB = new JCheckBox();
+        jLabel10 = new JLabel();
         legendPanel = new ChromosomeColorLegend();
-        showRefSeqCB = new javax.swing.JCheckBox();
-        shadeCenterCB = new javax.swing.JCheckBox();
-        showCovTrackCB = new javax.swing.JCheckBox();
-        samShadeMismatchedBaseCB = new javax.swing.JCheckBox();
-        samMinBaseQualityField = new javax.swing.JTextField();
-        samMaxBaseQualityField = new javax.swing.JTextField();
-        jLabel19 = new javax.swing.JLabel();
-        jLabel20 = new javax.swing.JLabel();
-        filterCB = new javax.swing.JCheckBox();
-        filterURL = new javax.swing.JTextField();
-        expressionPane = new javax.swing.JPanel();
-        jPanel8 = new javax.swing.JPanel();
-        expMapToGeneCB = new javax.swing.JRadioButton();
-        jLabel24 = new javax.swing.JLabel();
-        expMapToLociCB = new javax.swing.JRadioButton();
-        jLabel21 = new javax.swing.JLabel();
-        advancedPanel = new javax.swing.JPanel();
-        jPanel3 = new javax.swing.JPanel();
-        jPanel2 = new javax.swing.JPanel();
-        jLabel1 = new javax.swing.JLabel();
-        genomeServerURLTextField = new javax.swing.JTextField();
-        jLabel6 = new javax.swing.JLabel();
-        dataServerURLTextField = new javax.swing.JTextField();
-        editServerPropertiesCB = new javax.swing.JCheckBox();
-        clearGenomeCacheButton = new javax.swing.JButton();
-        jButton1 = new javax.swing.JButton();
-        jPanel7 = new javax.swing.JPanel();
-        enablePortCB = new javax.swing.JCheckBox();
-        portField = new javax.swing.JTextField();
-        jLabel22 = new javax.swing.JLabel();
-        jPanel9 = new javax.swing.JPanel();
-        useByteRangeCB = new javax.swing.JCheckBox();
-        jLabel25 = new javax.swing.JLabel();
-        okCancelButtonPanel = new com.jidesoft.dialog.ButtonPanel();
-        okButton = new javax.swing.JButton();
-        cancelButton = new javax.swing.JButton();
-
-        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+        filterFailedReadsCB = new JCheckBox();
+        label2 = new JLabel();
+        showSoftClippedCB = new JCheckBox();
+        expressionPane = new JPanel();
+        jPanel8 = new JPanel();
+        expMapToGeneCB = new JRadioButton();
+        jLabel24 = new JLabel();
+        expMapToLociCB = new JRadioButton();
+        jLabel21 = new JLabel();
+        advancedPanel = new JPanel();
+        jPanel3 = new JPanel();
+        jPanel2 = new JPanel();
+        jLabel1 = new JLabel();
+        genomeServerURLTextField = new JTextField();
+        jLabel6 = new JLabel();
+        dataServerURLTextField = new JTextField();
+        editServerPropertiesCB = new JCheckBox();
+        clearGenomeCacheButton = new JButton();
+        jButton1 = new JButton();
+        jPanel7 = new JPanel();
+        enablePortCB = new JCheckBox();
+        portField = new JTextField();
+        jLabel22 = new JLabel();
+        jPanel9 = new JPanel();
+        useByteRangeCB = new JCheckBox();
+        jLabel25 = new JLabel();
+        proxyPanel = new JPanel();
+        jPanel15 = new JPanel();
+        jPanel16 = new JPanel();
+        proxyUsernameField = new JTextField();
+        jLabel28 = new JLabel();
+        authenticateProxyCB = new JCheckBox();
+        jLabel29 = new JLabel();
+        proxyPasswordField = new JPasswordField();
+        jPanel17 = new JPanel();
+        proxyHostField = new JTextField();
+        proxyPortField = new JTextField();
+        jLabel27 = new JLabel();
+        jLabel23 = new JLabel();
+        useProxyCB = new JCheckBox();
+        label3 = new JLabel();
+        clearAllProxyButton = new JButton();
+        okCancelButtonPanel = new ButtonPanel();
+        okButton = new JButton();
+        cancelButton = new JButton();
+
+        //======== this ========
+        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
         setResizable(false);
-
-        generalPanel.setLayout(null);
-
-        missingDataExplanation.setFont(new java.awt.Font("Lucida Grande", 2, 12));
-        missingDataExplanation.setText("<html>Distinguish  regions with zero values from regions with no data on plots <br>(e.g. bar charts).  Regions with no data are indicated with a gray background."); // NOI18N
-
-        showMissingDataCB.setText("Distinguish Missing Data");
-        showMissingDataCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                showMissingDataCBActionPerformed(evt);
-            }
-        });
-
-        combinePanelsCB.setText("Combine Data and Feature Panels");
-        combinePanelsCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                combinePanelsCBActionPerformed(evt);
-            }
-        });
-
-        joinSegmentsCB.setText("Join Adjacent CopyNumber Segments");
-        joinSegmentsCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                joinSegmentsCBActionPerformed(evt);
-            }
-        });
-
-        showAttributesDisplayCheckBox.setText("Show Attribute Display");
-        showAttributesDisplayCheckBox.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                showAttributesDisplayCheckBoxActionPerformed(evt);
-            }
-        });
-
-        searchZoomCB.setText("Zoom to features");
-        searchZoomCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                searchZoomCBActionPerformed(evt);
-            }
-        });
-
-        missingDataExplanation6.setFont(new java.awt.Font("Lucida Grande", 2, 12));
-        missingDataExplanation6.setText("<html>This option applies to segmented copy number data only.  When selected, gaps between<br>adjacent segments are filled by extending segment endpoints."); // NOI18N
-
-        missingDataExplanation7.setFont(new java.awt.Font("Lucida Grande", 2, 12));
-        missingDataExplanation7.setText("<html>This option controls the behavior of feature searchs.  If true, the zoom level is changed as required to size the view to the feature size.  If false the zoom level is unchanged."); // NOI18N
-        missingDataExplanation7.setVerticalAlignment(javax.swing.SwingConstants.TOP);
-
-        org.jdesktop.layout.GroupLayout jPanel10Layout = new org.jdesktop.layout.GroupLayout(jPanel10);
-        jPanel10.setLayout(jPanel10Layout);
-        jPanel10Layout.setHorizontalGroup(
-                jPanel10Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel10Layout.createSequentialGroup()
-                        .addContainerGap()
-                        .add(jPanel10Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                .add(jPanel10Layout.createSequentialGroup()
-                                        .add(35, 35, 35)
-                                        .add(missingDataExplanation))
-                                .add(showMissingDataCB)
-                                .add(combinePanelsCB)
-                                .add(joinSegmentsCB)
-                                .add(searchZoomCB)
-                                .add(showAttributesDisplayCheckBox)
-                                .add(jPanel10Layout.createSequentialGroup()
-                                .add(44, 44, 44)
-                                .add(jPanel10Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
-                                .add(missingDataExplanation7, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 508, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                .add(missingDataExplanation6))))
-                        .addContainerGap(39, Short.MAX_VALUE))
-        );
-        jPanel10Layout.setVerticalGroup(
-                jPanel10Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel10Layout.createSequentialGroup()
-                        .addContainerGap()
-                        .add(showMissingDataCB)
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(missingDataExplanation)
-                        .add(18, 18, 18)
-                        .add(combinePanelsCB)
-                        .add(26, 26, 26)
-                        .add(joinSegmentsCB)
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(missingDataExplanation6)
-                        .add(18, 18, 18)
-                        .add(showAttributesDisplayCheckBox)
-                        .add(28, 28, 28)
-                        .add(searchZoomCB)
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(missingDataExplanation7, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 78, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                        .addContainerGap(45, Short.MAX_VALUE))
-        );
-
-        missingDataExplanation7.getAccessibleContext().setAccessibleName("<html>This option controls the zoom behavior when using search by feature.  If checked, the zoom level is changed as required to size the view width to exactly match the feature size.  If unchecked the zoom level is not changed.");
-
-        generalPanel.add(jPanel10);
-        jPanel10.setBounds(0, 0, 610, 420);
-
-        tabbedPane.addTab("General", generalPanel);
-
-        tracksPanel.setLayout(null);
-
-        jLabel5.setText("Default Track Height, Charts (Pixels)"); // NOI18N
-
-        defaultChartTrackHeightField.setText("40"); // NOI18N
-        defaultChartTrackHeightField.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                defaultChartTrackHeightFieldActionPerformed(evt);
-            }
-        });
-        defaultChartTrackHeightField.addFocusListener(new java.awt.event.FocusAdapter() {
-            public void focusLost(java.awt.event.FocusEvent evt) {
-                defaultChartTrackHeightFieldFocusLost(evt);
-            }
-        });
-
-        trackNameAttributeLabel.setText("Track Name Attribute"); // NOI18N
-
-        trackNameAttributeField.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                trackNameAttributeFieldActionPerformed(evt);
+        Container contentPane = getContentPane();
+        contentPane.setLayout(new BorderLayout());
+
+        //======== tabbedPane ========
+        {
+
+            //======== generalPanel ========
+            {
+                generalPanel.setLayout(null);
+
+                //======== jPanel10 ========
+                {
+
+                    //---- missingDataExplanation ----
+                    missingDataExplanation.setFont(new Font("Lucida Grande", Font.ITALIC, 12));
+                    missingDataExplanation.setText("<html>Distinguish  regions with zero values from regions with no data on plots <br>(e.g. bar charts).  Regions with no data are indicated with a gray background.");
+
+                    //---- showMissingDataCB ----
+                    showMissingDataCB.setText("Distinguish Missing Data");
+                    showMissingDataCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            showMissingDataCBActionPerformed(e);
+                        }
+                    });
+
+                    //---- combinePanelsCB ----
+                    combinePanelsCB.setText("Combine Data and Feature Panels");
+                    combinePanelsCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            combinePanelsCBActionPerformed(e);
+                        }
+                    });
+
+                    //---- joinSegmentsCB ----
+                    joinSegmentsCB.setText("Join Adjacent CopyNumber Segments");
+                    joinSegmentsCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            joinSegmentsCBActionPerformed(e);
+                        }
+                    });
+
+                    //---- showAttributesDisplayCheckBox ----
+                    showAttributesDisplayCheckBox.setText("Show Attribute Display");
+                    showAttributesDisplayCheckBox.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            showAttributesDisplayCheckBoxActionPerformed(e);
+                        }
+                    });
+
+                    //---- searchZoomCB ----
+                    searchZoomCB.setText("Zoom to features");
+                    searchZoomCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            searchZoomCBActionPerformed(e);
+                        }
+                    });
+
+                    //---- missingDataExplanation6 ----
+                    missingDataExplanation6.setFont(new Font("Lucida Grande", Font.ITALIC, 12));
+                    missingDataExplanation6.setText("<html>This option applies to segmented copy number data only.  When selected, gaps between<br>adjacent segments are filled by extending segment endpoints.");
+
+                    //---- missingDataExplanation7 ----
+                    missingDataExplanation7.setFont(new Font("Lucida Grande", Font.ITALIC, 12));
+                    missingDataExplanation7.setText("<html>This option controls the behavior of feature searchs.  If true, the zoom level is changed as required to size the view to the feature size.  If false the zoom level is unchanged.");
+                    missingDataExplanation7.setVerticalAlignment(SwingConstants.TOP);
+
+                    GroupLayout jPanel10Layout = new GroupLayout(jPanel10);
+                    jPanel10.setLayout(jPanel10Layout);
+                    jPanel10Layout.setHorizontalGroup(
+                            jPanel10Layout.createParallelGroup()
+                                    .add(jPanel10Layout.createSequentialGroup()
+                                    .addContainerGap()
+                                    .add(jPanel10Layout.createParallelGroup()
+                                            .add(jPanel10Layout.createSequentialGroup()
+                                                    .add(35, 35, 35)
+                                                    .add(missingDataExplanation, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                                            .add(showMissingDataCB)
+                                            .add(combinePanelsCB)
+                                            .add(joinSegmentsCB)
+                                            .add(searchZoomCB)
+                                            .add(showAttributesDisplayCheckBox)
+                                            .add(jPanel10Layout.createSequentialGroup()
+                                            .add(44, 44, 44)
+                                            .add(jPanel10Layout.createParallelGroup(GroupLayout.TRAILING)
+                                            .add(missingDataExplanation7, GroupLayout.PREFERRED_SIZE, 508, GroupLayout.PREFERRED_SIZE)
+                                            .add(missingDataExplanation6, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))))
+                                    .addContainerGap(50, Short.MAX_VALUE))
+                    );
+                    jPanel10Layout.setVerticalGroup(
+                            jPanel10Layout.createParallelGroup()
+                                    .add(jPanel10Layout.createSequentialGroup()
+                                    .addContainerGap()
+                                    .add(showMissingDataCB)
+                                    .addPreferredGap(LayoutStyle.RELATED)
+                                    .add(missingDataExplanation, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                                    .add(18, 18, 18)
+                                    .add(combinePanelsCB)
+                                    .add(26, 26, 26)
+                                    .add(joinSegmentsCB)
+                                    .addPreferredGap(LayoutStyle.RELATED)
+                                    .add(missingDataExplanation6, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                                    .add(18, 18, 18)
+                                    .add(showAttributesDisplayCheckBox)
+                                    .add(28, 28, 28)
+                                    .add(searchZoomCB)
+                                    .addPreferredGap(LayoutStyle.RELATED)
+                                    .add(missingDataExplanation7, GroupLayout.PREFERRED_SIZE, 78, GroupLayout.PREFERRED_SIZE)
+                                    .addContainerGap(53, Short.MAX_VALUE))
+                    );
+                }
+                generalPanel.add(jPanel10);
+                jPanel10.setBounds(0, 0, 610, 420);
+
+                { // compute preferred size
+                    Dimension preferredSize = new Dimension();
+                    for (int i = 0; i < generalPanel.getComponentCount(); i++) {
+                        Rectangle bounds = generalPanel.getComponent(i).getBounds();
+                        preferredSize.width = Math.max(bounds.x + bounds.width, preferredSize.width);
+                        preferredSize.height = Math.max(bounds.y + bounds.height, preferredSize.height);
+                    }
+                    Insets insets = generalPanel.getInsets();
+                    preferredSize.width += insets.right;
+                    preferredSize.height += insets.bottom;
+                    generalPanel.setMinimumSize(preferredSize);
+                    generalPanel.setPreferredSize(preferredSize);
+                }
             }
-        });
-        trackNameAttributeField.addFocusListener(new java.awt.event.FocusAdapter() {
-            public void focusLost(java.awt.event.FocusEvent evt) {
-                trackNameAttributeFieldFocusLost(evt);
+            tabbedPane.addTab("General", generalPanel);
+
+
+            //======== tracksPanel ========
+            {
+                tracksPanel.setLayout(null);
+
+                //======== jPanel6 ========
+                {
+
+                    //---- jLabel5 ----
+                    jLabel5.setText("Default Track Height, Charts (Pixels)");
+
+                    //---- defaultChartTrackHeightField ----
+                    defaultChartTrackHeightField.setText("40");
+                    defaultChartTrackHeightField.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            defaultChartTrackHeightFieldActionPerformed(e);
+                        }
+                    });
+                    defaultChartTrackHeightField.addFocusListener(new FocusAdapter() {
+                        @Override
+                        public void focusLost(FocusEvent e) {
+                            defaultChartTrackHeightFieldFocusLost(e);
+                        }
+                    });
+
+                    //---- trackNameAttributeLabel ----
+                    trackNameAttributeLabel.setText("Track Name Attribute");
+
+                    //---- trackNameAttributeField ----
+                    trackNameAttributeField.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            trackNameAttributeFieldActionPerformed(e);
+                        }
+                    });
+                    trackNameAttributeField.addFocusListener(new FocusAdapter() {
+                        @Override
+                        public void focusLost(FocusEvent e) {
+                            trackNameAttributeFieldFocusLost(e);
+                        }
+                    });
+
+                    //---- missingDataExplanation2 ----
+                    missingDataExplanation2.setFont(new Font("Lucida Grande", Font.ITALIC, 12));
+                    missingDataExplanation2.setText("<html>Name of an attribute to be used to label  tracks.  If provided tracks will be labeled with the corresponding attribute values from the sample information file");
+                    missingDataExplanation2.setVerticalAlignment(SwingConstants.TOP);
+
+                    //---- jLabel8 ----
+                    jLabel8.setText("Default Track Height, Other (Pixels)");
+
+                    //---- defaultTrackHeightField ----
+                    defaultTrackHeightField.setText("15");
+                    defaultTrackHeightField.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            defaultTrackHeightFieldActionPerformed(e);
+                        }
+                    });
+                    defaultTrackHeightField.addFocusListener(new FocusAdapter() {
+                        @Override
+                        public void focusLost(FocusEvent e) {
+                            defaultTrackHeightFieldFocusLost(e);
+                        }
+                    });
+
+                    //---- missingDataExplanation4 ----
+                    missingDataExplanation4.setFont(new Font("Lucida Grande", Font.ITALIC, 12));
+                    missingDataExplanation4.setText("<html>Default height of chart tracks (barcharts, scatterplots, etc)");
+
+                    //---- missingDataExplanation5 ----
+                    missingDataExplanation5.setFont(new Font("Lucida Grande", Font.ITALIC, 12));
+                    missingDataExplanation5.setText("<html>Default height of all other tracks");
+
+                    //---- missingDataExplanation3 ----
+                    missingDataExplanation3.setFont(new Font("Lucida Grande", Font.ITALIC, 12));
+                    missingDataExplanation3.setText("<html><i> If selected feature tracks are expanded by default.");
+
+                    //---- expandCB ----
+                    expandCB.setText("Expand Feature Tracks");
+                    expandCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            expandCBActionPerformed(e);
+                        }
+                    });
+
+                    //---- normalizeCoverageCB ----
+                    normalizeCoverageCB.setText("Normalize Coverage Data");
+                    normalizeCoverageCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            normalizeCoverageCBActionPerformed(e);
+                        }
+                    });
+                    normalizeCoverageCB.addFocusListener(new FocusAdapter() {
+                        @Override
+                        public void focusLost(FocusEvent e) {
+                            normalizeCoverageCBFocusLost(e);
+                        }
+                    });
+
+                    //---- missingDataExplanation8 ----
+                    missingDataExplanation8.setFont(new Font("Lucida Grande", Font.ITALIC, 12));
+                    missingDataExplanation8.setText("<html><i> Applies to coverage tracks computed with igvtools (.tdf files).  If selected coverage values are scaled by (1,000,000 / totalCount),  where totalCount is the total number of features or alignments.");
+                    missingDataExplanation8.setVerticalAlignment(SwingConstants.TOP);
+
+                    //---- expandIconCB ----
+                    expandIconCB.setText("Show Expand Icon");
+                    expandIconCB.setToolTipText("If checked displays an expand/collapse icon on feature tracks.");
+                    expandIconCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            expandIconCBActionPerformed(e);
+                        }
+                    });
+
+                    GroupLayout jPanel6Layout = new GroupLayout(jPanel6);
+                    jPanel6.setLayout(jPanel6Layout);
+                    jPanel6Layout.setHorizontalGroup(
+                            jPanel6Layout.createParallelGroup()
+                                    .add(jPanel6Layout.createSequentialGroup()
+                                    .add(jPanel6Layout.createParallelGroup()
+                                            .add(jPanel6Layout.createSequentialGroup()
+                                                    .addContainerGap()
+                                                    .add(jPanel6Layout.createParallelGroup()
+                                                    .add(jPanel6Layout.createSequentialGroup()
+                                                            .add(24, 24, 24)
+                                                            .add(missingDataExplanation4, GroupLayout.PREFERRED_SIZE, 354, GroupLayout.PREFERRED_SIZE))
+                                                    .add(jPanel6Layout.createSequentialGroup()
+                                                            .add(trackNameAttributeLabel)
+                                                            .addPreferredGap(LayoutStyle.RELATED)
+                                                            .add(trackNameAttributeField, GroupLayout.PREFERRED_SIZE, 216, GroupLayout.PREFERRED_SIZE))
+                                                    .add(jPanel6Layout.createSequentialGroup()
+                                                            .add(jLabel8)
+                                                            .add(39, 39, 39)
+                                                            .add(defaultTrackHeightField, GroupLayout.PREFERRED_SIZE, 57, GroupLayout.PREFERRED_SIZE))
+                                                    .add(jPanel6Layout.createSequentialGroup()
+                                                            .add(24, 24, 24)
+                                                            .add(missingDataExplanation5, GroupLayout.DEFAULT_SIZE, 1141, Short.MAX_VALUE))
+                                                    .add(jPanel6Layout.createSequentialGroup()
+                                                    .add(jLabel5)
+                                                    .add(36, 36, 36)
+                                                    .add(defaultChartTrackHeightField, GroupLayout.PREFERRED_SIZE, 57, GroupLayout.PREFERRED_SIZE))))
+                                            .add(jPanel6Layout.createSequentialGroup()
+                                                    .addContainerGap()
+                                                    .add(expandIconCB)
+                                                    .add(724, 724, 724)
+                                                    .add(missingDataExplanation3, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                                            .add(jPanel6Layout.createSequentialGroup()
+                                                    .add(63, 63, 63)
+                                                    .add(missingDataExplanation2, GroupLayout.PREFERRED_SIZE, 578, GroupLayout.PREFERRED_SIZE))
+                                            .add(jPanel6Layout.createSequentialGroup()
+                                                    .addContainerGap()
+                                                    .add(expandCB))
+                                            .add(jPanel6Layout.createSequentialGroup()
+                                                    .addContainerGap()
+                                                    .add(normalizeCoverageCB))
+                                            .add(jPanel6Layout.createSequentialGroup()
+                                            .add(50, 50, 50)
+                                            .add(missingDataExplanation8, GroupLayout.PREFERRED_SIZE, 608, GroupLayout.PREFERRED_SIZE)))
+                                    .addContainerGap())
+                    );
+                    jPanel6Layout.setVerticalGroup(
+                            jPanel6Layout.createParallelGroup()
+                                    .add(jPanel6Layout.createSequentialGroup()
+                                    .addContainerGap()
+                                    .add(jPanel6Layout.createParallelGroup(GroupLayout.BASELINE)
+                                            .add(jLabel5)
+                                            .add(defaultChartTrackHeightField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                                    .addPreferredGap(LayoutStyle.RELATED)
+                                    .add(missingDataExplanation4, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE)
+                                    .addPreferredGap(LayoutStyle.RELATED)
+                                    .add(jPanel6Layout.createParallelGroup(GroupLayout.BASELINE)
+                                            .add(jLabel8)
+                                            .add(defaultTrackHeightField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                                    .addPreferredGap(LayoutStyle.RELATED)
+                                    .add(missingDataExplanation5, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE)
+                                    .add(26, 26, 26)
+                                    .add(jPanel6Layout.createParallelGroup(GroupLayout.BASELINE)
+                                            .add(trackNameAttributeLabel)
+                                            .add(trackNameAttributeField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                                    .addPreferredGap(LayoutStyle.RELATED)
+                                    .add(missingDataExplanation2, GroupLayout.PREFERRED_SIZE, 54, GroupLayout.PREFERRED_SIZE)
+                                    .add(28, 28, 28)
+                                    .add(expandCB)
+                                    .add(23, 23, 23)
+                                    .add(jPanel6Layout.createParallelGroup()
+                                            .add(missingDataExplanation3, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                                            .add(expandIconCB))
+                                    .addPreferredGap(LayoutStyle.RELATED, 31, Short.MAX_VALUE)
+                                    .add(normalizeCoverageCB)
+                                    .add(18, 18, 18)
+                                    .add(missingDataExplanation8, GroupLayout.PREFERRED_SIZE, 52, GroupLayout.PREFERRED_SIZE)
+                                    .add(15, 15, 15))
+                    );
+                }
+                tracksPanel.add(jPanel6);
+                jPanel6.setBounds(40, 20, 690, 480);
+
+                { // compute preferred size
+                    Dimension preferredSize = new Dimension();
+                    for (int i = 0; i < tracksPanel.getComponentCount(); i++) {
+                        Rectangle bounds = tracksPanel.getComponent(i).getBounds();
+                        preferredSize.width = Math.max(bounds.x + bounds.width, preferredSize.width);
+                        preferredSize.height = Math.max(bounds.y + bounds.height, preferredSize.height);
+                    }
+                    Insets insets = tracksPanel.getInsets();
+                    preferredSize.width += insets.right;
+                    preferredSize.height += insets.bottom;
+                    tracksPanel.setMinimumSize(preferredSize);
+                    tracksPanel.setPreferredSize(preferredSize);
+                }
             }
-        });
-
-        missingDataExplanation2.setFont(new java.awt.Font("Lucida Grande", 2, 12));
-        missingDataExplanation2.setText("<html>Name of an attribute to be used to label  tracks.  If provided tracks will be labeled with the corresponding attribute values from the sample information file"); // NOI18N
-
-        jLabel8.setText("Default Track Height, Other (Pixels)"); // NOI18N
+            tabbedPane.addTab("Tracks", tracksPanel);
+
+
+            //======== overlaysPanel ========
+            {
+
+                //======== jPanel5 ========
+                {
+
+                    //---- jLabel3 ----
+                    jLabel3.setText("Overlay tracks based on attribute:");
+
+                    //---- overlayAttributeTextField ----
+                    overlayAttributeTextField.setText("LINKING_ID");
+                    overlayAttributeTextField.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            overlayAttributeTextFieldActionPerformed(e);
+                        }
+                    });
+                    overlayAttributeTextField.addFocusListener(new FocusAdapter() {
+                        @Override
+                        public void focusLost(FocusEvent e) {
+                            overlayAttributeTextFieldFocusLost(e);
+                        }
+                    });
+                    overlayAttributeTextField.addKeyListener(new KeyAdapter() {
+                        @Override
+                        public void keyTyped(KeyEvent e) {
+                            overlayAttributeTextFieldKeyTyped(e);
+                        }
+                    });
+
+                    //---- overlayTrackCB ----
+                    overlayTrackCB.setSelected(true);
+                    overlayTrackCB.setText("Overlay mutation tracks");
+                    overlayTrackCB.setActionCommand("overlayTracksCB");
+                    overlayTrackCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            overlayTrackCBActionPerformed(e);
+                        }
+                    });
+
+                    //---- jLabel2 ----
+                    jLabel2.setFont(new Font("Lucida Grande", Font.ITALIC, 12));
+
+                    //---- displayTracksCB ----
+                    displayTracksCB.setText("Display mutation data as distinct tracks");
+                    displayTracksCB.setActionCommand("displayTracksCB");
+                    displayTracksCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            displayTracksCBActionPerformed(e);
+                        }
+                    });
+
+                    //---- jLabel4 ----
+                    jLabel4.setFont(new Font("Lucida Grande", Font.ITALIC, 12));
+
+                    //---- colorOverlyCB ----
+                    colorOverlyCB.setText("Color code overlay");
+                    colorOverlyCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            colorOverlyCBActionPerformed(e);
+                        }
+                    });
+
+                    //---- chooseOverlayColorsButton ----
+                    chooseOverlayColorsButton.setForeground(new Color(0, 0, 247));
+                    chooseOverlayColorsButton.setText("Choose colors");
+                    chooseOverlayColorsButton.setFont(new Font("Lucida Grande", Font.ITALIC, 12));
+                    chooseOverlayColorsButton.setVerticalAlignment(SwingConstants.BOTTOM);
+                    chooseOverlayColorsButton.setVerticalTextPosition(SwingConstants.BOTTOM);
+                    chooseOverlayColorsButton.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            chooseOverlayColorsButtonActionPerformed(e);
+                        }
+                    });
+
+                    GroupLayout jPanel5Layout = new GroupLayout(jPanel5);
+                    jPanel5.setLayout(jPanel5Layout);
+                    jPanel5Layout.setHorizontalGroup(
+                            jPanel5Layout.createParallelGroup()
+                                    .add(jPanel5Layout.createSequentialGroup()
+                                    .addContainerGap()
+                                    .add(jPanel5Layout.createParallelGroup()
+                                            .add(displayTracksCB)
+                                            .add(jLabel4)
+                                            .add(overlayTrackCB)
+                                            .add(jLabel2)
+                                            .add(jPanel5Layout.createSequentialGroup()
+                                            .add(59, 59, 59)
+                                            .add(jPanel5Layout.createParallelGroup()
+                                            .add(jPanel5Layout.createSequentialGroup()
+                                                    .add(colorOverlyCB)
+                                                    .addPreferredGap(LayoutStyle.RELATED)
+                                                    .add(chooseOverlayColorsButton, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                                            .add(jPanel5Layout.createSequentialGroup()
+                                            .add(jLabel3)
+                                            .addPreferredGap(LayoutStyle.RELATED)
+                                            .add(overlayAttributeTextField, GroupLayout.PREFERRED_SIZE, 228, GroupLayout.PREFERRED_SIZE)))))
+                                    .addContainerGap())
+                    );
+                    jPanel5Layout.setVerticalGroup(
+                            jPanel5Layout.createParallelGroup()
+                                    .add(jPanel5Layout.createSequentialGroup()
+                                    .addContainerGap()
+                                    .add(jLabel2)
+                                    .addPreferredGap(LayoutStyle.RELATED)
+                                    .add(jLabel4)
+                                    .add(39, 39, 39)
+                                    .add(overlayTrackCB)
+                                    .addPreferredGap(LayoutStyle.RELATED)
+                                    .add(jPanel5Layout.createParallelGroup(GroupLayout.BASELINE)
+                                            .add(jLabel3)
+                                            .add(overlayAttributeTextField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                                    .add(6, 6, 6)
+                                    .add(jPanel5Layout.createParallelGroup(GroupLayout.TRAILING)
+                                            .add(colorOverlyCB)
+                                            .add(chooseOverlayColorsButton, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                                    .add(36, 36, 36)
+                                    .add(displayTracksCB)
+                                    .addContainerGap(30, Short.MAX_VALUE))
+                    );
+                }
 
-        defaultTrackHeightField.setText("15"); // NOI18N
-        defaultTrackHeightField.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                defaultTrackHeightFieldActionPerformed(evt);
+                GroupLayout overlaysPanelLayout = new GroupLayout(overlaysPanel);
+                overlaysPanel.setLayout(overlaysPanelLayout);
+                overlaysPanelLayout.setHorizontalGroup(
+                        overlaysPanelLayout.createParallelGroup()
+                                .add(overlaysPanelLayout.createSequentialGroup()
+                                .add(28, 28, 28)
+                                .add(jPanel5, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                                .addContainerGap(235, Short.MAX_VALUE))
+                );
+                overlaysPanelLayout.setVerticalGroup(
+                        overlaysPanelLayout.createParallelGroup()
+                                .add(overlaysPanelLayout.createSequentialGroup()
+                                .add(55, 55, 55)
+                                .add(jPanel5, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                                .addContainerGap(225, Short.MAX_VALUE))
+                );
+            }
+            tabbedPane.addTab("Mutations", overlaysPanel);
+
+
+            //======== chartPanel ========
+            {
+                chartPanel.setLayout(null);
+
+                //======== jPanel4 ========
+                {
+
+                    //---- topBorderCB ----
+                    topBorderCB.setText("Draw Top Border");
+                    topBorderCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            topBorderCBActionPerformed(e);
+                        }
+                    });
+
+                    //---- label1 ----
+                    label1.setFont(label1.getFont());
+                    label1.setText("Default settings for barcharts and scatterplots:");
+
+                    //---- chartDrawTrackNameCB ----
+                    chartDrawTrackNameCB.setText("Draw Track Label");
+                    chartDrawTrackNameCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            chartDrawTrackNameCBActionPerformed(e);
+                        }
+                    });
+
+                    //---- bottomBorderCB ----
+                    bottomBorderCB.setText("Draw Bottom Border");
+                    bottomBorderCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            bottomBorderCBActionPerformed(e);
+                        }
+                    });
+
+                    //---- jLabel7 ----
+                    jLabel7.setText("<html><i>If selected charts are dynamically rescaled to the range of the data in view.");
+
+                    //---- colorBordersCB ----
+                    colorBordersCB.setText("Color Borders");
+                    colorBordersCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            colorBordersCBActionPerformed(e);
+                        }
+                    });
+
+                    //---- labelYAxisCB ----
+                    labelYAxisCB.setText("Label Y Axis");
+                    labelYAxisCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            labelYAxisCBActionPerformed(e);
+                        }
+                    });
+
+                    //---- autoscaleCB ----
+                    autoscaleCB.setText("Continuous Autoscale");
+                    autoscaleCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            autoscaleCBActionPerformed(e);
+                        }
+                    });
+
+                    //---- jLabel9 ----
+                    jLabel9.setText("<html><i>Draw a label centered over the track provided<br>the track height is at least 25 pixels. ");
+
+                    //---- showDatarangeCB ----
+                    showDatarangeCB.setText("Show Data Range");
+                    showDatarangeCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            showDatarangeCBActionPerformed(e);
+                        }
+                    });
+                    showDatarangeCB.addFocusListener(new FocusAdapter() {
+                        @Override
+                        public void focusLost(FocusEvent e) {
+                            showDatarangeCBFocusLost(e);
+                        }
+                    });
+
+                    GroupLayout jPanel4Layout = new GroupLayout(jPanel4);
+                    jPanel4.setLayout(jPanel4Layout);
+                    jPanel4Layout.setHorizontalGroup(
+                            jPanel4Layout.createParallelGroup()
+                                    .add(jPanel4Layout.createParallelGroup()
+                                            .add(GroupLayout.TRAILING, jPanel4Layout.createSequentialGroup()
+                                            .addContainerGap(221, Short.MAX_VALUE)
+                                            .add(jLabel9, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                                            .add(84, 84, 84)))
+                                    .add(jPanel4Layout.createSequentialGroup()
+                                    .addContainerGap()
+                                    .add(jPanel4Layout.createParallelGroup()
+                                            .add(label1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                                            .add(jPanel4Layout.createSequentialGroup()
+                                            .add(20, 20, 20)
+                                            .add(jPanel4Layout.createParallelGroup()
+                                            .add(jPanel4Layout.createSequentialGroup()
+                                                    .add(jPanel4Layout.createParallelGroup()
+                                                            .add(autoscaleCB)
+                                                            .add(showDatarangeCB))
+                                                    .add(18, 18, 18)
+                                                    .add(jLabel7, GroupLayout.DEFAULT_SIZE, 371, Short.MAX_VALUE))
+                                            .add(jPanel4Layout.createSequentialGroup()
+                                            .add(jPanel4Layout.createParallelGroup()
+                                                    .add(topBorderCB)
+                                                    .add(colorBordersCB)
+                                                    .add(bottomBorderCB)
+                                                    .add(labelYAxisCB)
+                                                    .add(chartDrawTrackNameCB))
+                                            .addPreferredGap(LayoutStyle.RELATED, 403, Short.MAX_VALUE)))))
+                                    .addContainerGap())
+                    );
+                    jPanel4Layout.setVerticalGroup(
+                            jPanel4Layout.createParallelGroup()
+                                    .add(jPanel4Layout.createParallelGroup()
+                                            .add(jPanel4Layout.createSequentialGroup()
+                                            .add(131, 131, 131)
+                                            .add(jLabel9, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                                            .addContainerGap(181, Short.MAX_VALUE)))
+                                    .add(jPanel4Layout.createSequentialGroup()
+                                    .addContainerGap()
+                                    .add(label1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                                    .addPreferredGap(LayoutStyle.RELATED)
+                                    .add(topBorderCB)
+                                    .addPreferredGap(LayoutStyle.RELATED)
+                                    .add(bottomBorderCB)
+                                    .add(7, 7, 7)
+                                    .add(colorBordersCB)
+                                    .add(18, 18, 18)
+                                    .add(chartDrawTrackNameCB)
+                                    .add(23, 23, 23)
+                                    .add(labelYAxisCB)
+                                    .add(18, 18, 18)
+                                    .add(jPanel4Layout.createParallelGroup(GroupLayout.BASELINE)
+                                            .add(autoscaleCB)
+                                            .add(jLabel7, GroupLayout.PREFERRED_SIZE, 50, GroupLayout.PREFERRED_SIZE))
+                                    .addPreferredGap(LayoutStyle.UNRELATED)
+                                    .add(showDatarangeCB)
+                                    .add(36, 36, 36))
+                    );
+                }
+                chartPanel.add(jPanel4);
+                jPanel4.setBounds(20, 30, 590, 340);
+
+                { // compute preferred size
+                    Dimension preferredSize = new Dimension();
+                    for (int i = 0; i < chartPanel.getComponentCount(); i++) {
+                        Rectangle bounds = chartPanel.getComponent(i).getBounds();
+                        preferredSize.width = Math.max(bounds.x + bounds.width, preferredSize.width);
+                        preferredSize.height = Math.max(bounds.y + bounds.height, preferredSize.height);
+                    }
+                    Insets insets = chartPanel.getInsets();
+                    preferredSize.width += insets.right;
+                    preferredSize.height += insets.bottom;
+                    chartPanel.setMinimumSize(preferredSize);
+                    chartPanel.setPreferredSize(preferredSize);
+                }
             }
-        });
-        defaultTrackHeightField.addFocusListener(new java.awt.event.FocusAdapter() {
-            public void focusLost(java.awt.event.FocusEvent evt) {
-                defaultTrackHeightFieldFocusLost(evt);
+            tabbedPane.addTab("Charts", chartPanel);
+
+
+            //======== alignmentPanel ========
+            {
+                alignmentPanel.setLayout(null);
+
+                //======== jPanel1 ========
+                {
+                    jPanel1.setLayout(null);
+
+                    //======== jPanel11 ========
+                    {
+                        jPanel11.setLayout(null);
+
+                        //---- samMaxLevelsField ----
+                        samMaxLevelsField.setText("jTextField1");
+                        samMaxLevelsField.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                samMaxLevelFieldActionPerformed(e);
+                            }
+                        });
+                        samMaxLevelsField.addFocusListener(new FocusAdapter() {
+                            @Override
+                            public void focusLost(FocusEvent e) {
+                                samMaxLevelFieldFocusLost(e);
+                            }
+                        });
+                        jPanel11.add(samMaxLevelsField);
+                        samMaxLevelsField.setBounds(new Rectangle(new Point(206, 41), samMaxLevelsField.getPreferredSize()));
+
+                        //---- snpThresholdField ----
+                        snpThresholdField.setText("0");
+                        snpThresholdField.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                snpThresholdFieldActionPerformed(e);
+                            }
+                        });
+                        snpThresholdField.addFocusListener(new FocusAdapter() {
+                            @Override
+                            public void focusLost(FocusEvent e) {
+                                snpThresholdFieldFocusLost(e);
+                            }
+                        });
+                        jPanel11.add(snpThresholdField);
+                        snpThresholdField.setBounds(206, 144, 84, snpThresholdField.getPreferredSize().height);
+
+                        //---- jLabel11 ----
+                        jLabel11.setText("Visibility range threshold (kb)");
+                        jPanel11.add(jLabel11);
+                        jLabel11.setBounds(new Rectangle(new Point(6, 12), jLabel11.getPreferredSize()));
+
+                        //---- jLabel26 ----
+                        jLabel26.setText("Coverage allele-freq threshold");
+                        jPanel11.add(jLabel26);
+                        jLabel26.setBounds(6, 150, 200, jLabel26.getPreferredSize().height);
+
+                        //---- jLabel17 ----
+                        jLabel17.setText("Insert size flagging threshold:");
+                        jPanel11.add(jLabel17);
+                        jLabel17.setBounds(6, 116, 200, jLabel17.getPreferredSize().height);
+
+                        //---- jLabel16 ----
+                        jLabel16.setText("<html><i>Reads with qualities  below the threshold are not shown.");
+                        jPanel11.add(jLabel16);
+                        jLabel16.setBounds(new Rectangle(new Point(296, 82), jLabel16.getPreferredSize()));
+
+                        //---- mappingQualityThresholdField ----
+                        mappingQualityThresholdField.setText("0");
+                        mappingQualityThresholdField.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                mappingQualityThresholdFieldActionPerformed(e);
+                            }
+                        });
+                        mappingQualityThresholdField.addFocusListener(new FocusAdapter() {
+                            @Override
+                            public void focusLost(FocusEvent e) {
+                                mappingQualityThresholdFieldFocusLost(e);
+                            }
+                        });
+                        jPanel11.add(mappingQualityThresholdField);
+                        mappingQualityThresholdField.setBounds(206, 76, 84, mappingQualityThresholdField.getPreferredSize().height);
+
+                        //---- jLabel14 ----
+                        jLabel14.setText("<html><i>Maximum depth of reads to display.");
+                        jPanel11.add(jLabel14);
+                        jLabel14.setBounds(296, 40, 390, 30);
+
+                        //---- insertSizeThresholdField ----
+                        insertSizeThresholdField.setText("0");
+                        insertSizeThresholdField.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                insertSizeThresholdFieldActionPerformed(e);
+                            }
+                        });
+                        insertSizeThresholdField.addFocusListener(new FocusAdapter() {
+                            @Override
+                            public void focusLost(FocusEvent e) {
+                                insertSizeThresholdFieldFocusLost(e);
+                            }
+                        });
+                        jPanel11.add(insertSizeThresholdField);
+                        insertSizeThresholdField.setBounds(206, 110, 84, insertSizeThresholdField.getPreferredSize().height);
+
+                        //---- jLabel13 ----
+                        jLabel13.setText("Maximum read depth:");
+                        jPanel11.add(jLabel13);
+                        jLabel13.setBounds(new Rectangle(new Point(6, 47), jLabel13.getPreferredSize()));
+
+                        //---- jLabel15 ----
+                        jLabel15.setText("Mapping quality threshold:");
+                        jPanel11.add(jLabel15);
+                        jLabel15.setBounds(new Rectangle(new Point(6, 82), jLabel15.getPreferredSize()));
+
+                        //---- jLabel18 ----
+                        jLabel18.setText("<html><i>Paired end alignments with insert sizes > this value are flagged.");
+                        jPanel11.add(jLabel18);
+                        jLabel18.setBounds(296, 110, 414, 40);
+
+                        //---- samMaxWindowSizeField ----
+                        samMaxWindowSizeField.setText("jTextField1");
+                        samMaxWindowSizeField.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                samMaxWindowSizeFieldActionPerformed(e);
+                            }
+                        });
+                        samMaxWindowSizeField.addFocusListener(new FocusAdapter() {
+                            @Override
+                            public void focusLost(FocusEvent e) {
+                                samMaxWindowSizeFieldFocusLost(e);
+                            }
+                        });
+                        jPanel11.add(samMaxWindowSizeField);
+                        samMaxWindowSizeField.setBounds(new Rectangle(new Point(206, 6), samMaxWindowSizeField.getPreferredSize()));
+
+                        //---- jLabel12 ----
+                        jLabel12.setText("<html><i>Nominal window size at which alignments become visible");
+                        jPanel11.add(jLabel12);
+                        jLabel12.setBounds(new Rectangle(new Point(296, 12), jLabel12.getPreferredSize()));
+
+                        { // compute preferred size
+                            Dimension preferredSize = new Dimension();
+                            for (int i = 0; i < jPanel11.getComponentCount(); i++) {
+                                Rectangle bounds = jPanel11.getComponent(i).getBounds();
+                                preferredSize.width = Math.max(bounds.x + bounds.width, preferredSize.width);
+                                preferredSize.height = Math.max(bounds.y + bounds.height, preferredSize.height);
+                            }
+                            Insets insets = jPanel11.getInsets();
+                            preferredSize.width += insets.right;
+                            preferredSize.height += insets.bottom;
+                            jPanel11.setMinimumSize(preferredSize);
+                            jPanel11.setPreferredSize(preferredSize);
+                        }
+                    }
+                    jPanel1.add(jPanel11);
+                    jPanel11.setBounds(new Rectangle(new Point(6, 20), jPanel11.getPreferredSize()));
+
+                    //======== jPanel12 ========
+                    {
+                        jPanel12.setLayout(null);
+
+                        //---- samMinBaseQualityField ----
+                        samMinBaseQualityField.setText("0");
+                        samMinBaseQualityField.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                samMinBaseQualityFieldActionPerformed(e);
+                            }
+                        });
+                        samMinBaseQualityField.addFocusListener(new FocusAdapter() {
+                            @Override
+                            public void focusLost(FocusEvent e) {
+                                samMinBaseQualityFieldFocusLost(e);
+                            }
+                        });
+                        jPanel12.add(samMinBaseQualityField);
+                        samMinBaseQualityField.setBounds(325, 140, 50, samMinBaseQualityField.getPreferredSize().height);
+
+                        //---- samShadeMismatchedBaseCB ----
+                        samShadeMismatchedBaseCB.setText("Shade mismatched bases by quality. ");
+                        samShadeMismatchedBaseCB.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                samShadeMismatchedBaseCBActionPerformed(e);
+                            }
+                        });
+                        jPanel12.add(samShadeMismatchedBaseCB);
+                        samShadeMismatchedBaseCB.setBounds(6, 146, 290, samShadeMismatchedBaseCB.getPreferredSize().height);
+
+                        //---- samMaxBaseQualityField ----
+                        samMaxBaseQualityField.setText("0");
+                        samMaxBaseQualityField.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                samMaxBaseQualityFieldActionPerformed(e);
+                            }
+                        });
+                        samMaxBaseQualityField.addFocusListener(new FocusAdapter() {
+                            @Override
+                            public void focusLost(FocusEvent e) {
+                                samMaxBaseQualityFieldFocusLost(e);
+                            }
+                        });
+                        jPanel12.add(samMaxBaseQualityField);
+                        samMaxBaseQualityField.setBounds(455, 140, 50, samMaxBaseQualityField.getPreferredSize().height);
+
+                        //---- showCovTrackCB ----
+                        showCovTrackCB.setText("Show coverage track");
+                        showCovTrackCB.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                showCovTrackCBActionPerformed(e);
+                            }
+                        });
+                        jPanel12.add(showCovTrackCB);
+                        showCovTrackCB.setBounds(385, 76, 270, showCovTrackCB.getPreferredSize().height);
+
+                        //---- samFilterDuplicatesCB ----
+                        samFilterDuplicatesCB.setText("Filter duplicate reads");
+                        samFilterDuplicatesCB.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                samShowDuplicatesCBActionPerformed(e);
+                            }
+                        });
+                        jPanel12.add(samFilterDuplicatesCB);
+                        samFilterDuplicatesCB.setBounds(6, 6, 290, samFilterDuplicatesCB.getPreferredSize().height);
+
+                        //---- showRefSeqCB ----
+                        showRefSeqCB.setText("Show reference sequence");
+                        showRefSeqCB.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                showRefSeqCBActionPerformed(e);
+                            }
+                        });
+                        jPanel12.add(showRefSeqCB);
+                        showRefSeqCB.setBounds(385, 6, 270, showRefSeqCB.getPreferredSize().height);
+
+                        //---- jLabel19 ----
+                        jLabel19.setText("Min: ");
+                        jPanel12.add(jLabel19);
+                        jLabel19.setBounds(new Rectangle(new Point(285, 145), jLabel19.getPreferredSize()));
+
+                        //---- filterCB ----
+                        filterCB.setText("Filter alignments");
+                        filterCB.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                filterCBActionPerformed(e);
+                            }
+                        });
+                        jPanel12.add(filterCB);
+                        filterCB.setBounds(6, 181, 144, filterCB.getPreferredSize().height);
+
+                        //---- filterURL ----
+                        filterURL.setText("URL or path to filter file");
+                        filterURL.setEnabled(false);
+                        filterURL.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                filterURLActionPerformed(e);
+                            }
+                        });
+                        filterURL.addFocusListener(new FocusAdapter() {
+                            @Override
+                            public void focusLost(FocusEvent e) {
+                                filterURLFocusLost(e);
+                            }
+                        });
+                        jPanel12.add(filterURL);
+                        filterURL.setBounds(190, 185, 482, filterURL.getPreferredSize().height);
+
+                        //---- samFlagUnmappedPairCB ----
+                        samFlagUnmappedPairCB.setText("Flag unmapped pairs");
+                        samFlagUnmappedPairCB.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                samFlagUnmappedPairCBActionPerformed(e);
+                            }
+                        });
+                        jPanel12.add(samFlagUnmappedPairCB);
+                        samFlagUnmappedPairCB.setBounds(6, 76, 310, samFlagUnmappedPairCB.getPreferredSize().height);
+
+                        //---- shadeCenterCB ----
+                        shadeCenterCB.setText("Shade alignments intersecting center");
+                        shadeCenterCB.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                shadeCenterCBActionPerformed(e);
+                            }
+                        });
+                        jPanel12.add(shadeCenterCB);
+                        shadeCenterCB.setBounds(385, 41, 450, shadeCenterCB.getPreferredSize().height);
+
+                        //---- jLabel10 ----
+                        jLabel10.setText("<html>Chromosome color legend &nbsp;&nbsp;&nbsp;<i>Used to flag paired end reads with mates on other chromosomes");
+                        jPanel12.add(jLabel10);
+                        jLabel10.setBounds(5, 230, 640, 30);
+
+                        //======== legendPanel ========
+                        {
+
+                            GroupLayout legendPanelLayout = new GroupLayout(legendPanel);
+                            legendPanel.setLayout(legendPanelLayout);
+                            legendPanelLayout.setHorizontalGroup(
+                                    legendPanelLayout.createParallelGroup()
+                                            .add(0, 602, Short.MAX_VALUE)
+                            );
+                            legendPanelLayout.setVerticalGroup(
+                                    legendPanelLayout.createParallelGroup()
+                                            .add(0, 25, Short.MAX_VALUE)
+                            );
+                        }
+                        jPanel12.add(legendPanel);
+                        legendPanel.setBounds(new Rectangle(new Point(5, 270), legendPanel.getPreferredSize()));
+
+                        //---- filterFailedReadsCB ----
+                        filterFailedReadsCB.setText("Filter vendor failed reads");
+                        filterFailedReadsCB.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                filterVendorFailedReadsCBActionPerformed(e);
+                            }
+                        });
+                        jPanel12.add(filterFailedReadsCB);
+                        filterFailedReadsCB.setBounds(new Rectangle(new Point(6, 41), filterFailedReadsCB.getPreferredSize()));
+
+                        //---- label2 ----
+                        label2.setText("Max:");
+                        jPanel12.add(label2);
+                        label2.setBounds(new Rectangle(new Point(400, 145), label2.getPreferredSize()));
+
+                        //---- showSoftClippedCB ----
+                        showSoftClippedCB.setText("Show soft-clipped bases");
+                        showSoftClippedCB.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                showSoftClippedCBActionPerformed(e);
+                            }
+                        });
+                        jPanel12.add(showSoftClippedCB);
+                        showSoftClippedCB.setBounds(new Rectangle(new Point(6, 111), showSoftClippedCB.getPreferredSize()));
+
+                        { // compute preferred size
+                            Dimension preferredSize = new Dimension();
+                            for (int i = 0; i < jPanel12.getComponentCount(); i++) {
+                                Rectangle bounds = jPanel12.getComponent(i).getBounds();
+                                preferredSize.width = Math.max(bounds.x + bounds.width, preferredSize.width);
+                                preferredSize.height = Math.max(bounds.y + bounds.height, preferredSize.height);
+                            }
+                            Insets insets = jPanel12.getInsets();
+                            preferredSize.width += insets.right;
+                            preferredSize.height += insets.bottom;
+                            jPanel12.setMinimumSize(preferredSize);
+                            jPanel12.setPreferredSize(preferredSize);
+                        }
+                    }
+                    jPanel1.add(jPanel12);
+                    jPanel12.setBounds(6, 198, 740, 297);
+
+                    { // compute preferred size
+                        Dimension preferredSize = new Dimension();
+                        for (int i = 0; i < jPanel1.getComponentCount(); i++) {
+                            Rectangle bounds = jPanel1.getComponent(i).getBounds();
+                            preferredSize.width = Math.max(bounds.x + bounds.width, preferredSize.width);
+                            preferredSize.height = Math.max(bounds.y + bounds.height, preferredSize.height);
+                        }
+                        Insets insets = jPanel1.getInsets();
+                        preferredSize.width += insets.right;
+                        preferredSize.height += insets.bottom;
+                        jPanel1.setMinimumSize(preferredSize);
+                        jPanel1.setPreferredSize(preferredSize);
+                    }
+                }
+                alignmentPanel.add(jPanel1);
+                jPanel1.setBounds(0, 0, 760, 510);
+
+                { // compute preferred size
+                    Dimension preferredSize = new Dimension();
+                    for (int i = 0; i < alignmentPanel.getComponentCount(); i++) {
+                        Rectangle bounds = alignmentPanel.getComponent(i).getBounds();
+                        preferredSize.width = Math.max(bounds.x + bounds.width, preferredSize.width);
+                        preferredSize.height = Math.max(bounds.y + bounds.height, preferredSize.height);
+                    }
+                    Insets insets = alignmentPanel.getInsets();
+                    preferredSize.width += insets.right;
+                    preferredSize.height += insets.bottom;
+                    alignmentPanel.setMinimumSize(preferredSize);
+                    alignmentPanel.setPreferredSize(preferredSize);
+                }
             }
-        });
-
-        missingDataExplanation4.setFont(new java.awt.Font("Lucida Grande", 2, 12));
-        missingDataExplanation4.setText("<html>Default height of chart tracks (barcharts, scatterplots, etc)"); // NOI18N
-
-        missingDataExplanation5.setFont(new java.awt.Font("Lucida Grande", 2, 12));
-        missingDataExplanation5.setText("<html>Default height of all other tracks"); // NOI18N
-
-        jLabel23.setText("Expand Feature Tracks"); // NOI18N
-
-        missingDataExplanation3.setFont(new java.awt.Font("Lucida Grande", 2, 12));
-        missingDataExplanation3.setText("<html><i> If selected feature tracks are expanded by default."); // NOI18N
-
-        expandCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                expandCBActionPerformed(evt);
+            tabbedPane.addTab("Alignments", alignmentPanel);
+
+
+            //======== expressionPane ========
+            {
+                expressionPane.setLayout(null);
+
+                //======== jPanel8 ========
+                {
+
+                    //---- expMapToGeneCB ----
+                    expMapToGeneCB.setText("Map probes to genes");
+                    expMapToGeneCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            expMapToGeneCBActionPerformed(e);
+                        }
+                    });
+
+                    //---- jLabel24 ----
+                    jLabel24.setText("Expression probe mapping options: ");
+
+                    //---- expMapToLociCB ----
+                    expMapToLociCB.setText("<html>Map probes to target loci");
+                    expMapToLociCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            expMapToLociCBActionPerformed(e);
+                        }
+                    });
+
+                    //---- jLabel21 ----
+                    jLabel21.setText("<html><i>Note: Changes will not affect currently loaded datasets.");
+
+                    GroupLayout jPanel8Layout = new GroupLayout(jPanel8);
+                    jPanel8.setLayout(jPanel8Layout);
+                    jPanel8Layout.setHorizontalGroup(
+                            jPanel8Layout.createParallelGroup()
+                                    .add(jPanel8Layout.createSequentialGroup()
+                                    .add(jPanel8Layout.createParallelGroup()
+                                            .add(jPanel8Layout.createSequentialGroup()
+                                                    .add(45, 45, 45)
+                                                    .add(jPanel8Layout.createParallelGroup()
+                                                    .add(expMapToLociCB, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                                                    .add(expMapToGeneCB)))
+                                            .add(jPanel8Layout.createSequentialGroup()
+                                            .addContainerGap()
+                                            .add(jPanel8Layout.createParallelGroup()
+                                            .add(jPanel8Layout.createSequentialGroup()
+                                                    .add(24, 24, 24)
+                                                    .add(jLabel21, GroupLayout.PREFERRED_SIZE, 497, GroupLayout.PREFERRED_SIZE))
+                                            .add(jLabel24))))
+                                    .addContainerGap(193, Short.MAX_VALUE))
+                    );
+                    jPanel8Layout.setVerticalGroup(
+                            jPanel8Layout.createParallelGroup()
+                                    .add(jPanel8Layout.createSequentialGroup()
+                                    .addContainerGap()
+                                    .add(jLabel24)
+                                    .addPreferredGap(LayoutStyle.RELATED)
+                                    .add(jLabel21, GroupLayout.PREFERRED_SIZE, 44, GroupLayout.PREFERRED_SIZE)
+                                    .addPreferredGap(LayoutStyle.RELATED)
+                                    .add(expMapToLociCB, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                                    .add(14, 14, 14)
+                                    .add(expMapToGeneCB)
+                                    .addContainerGap(172, Short.MAX_VALUE))
+                    );
+                }
+                expressionPane.add(jPanel8);
+                jPanel8.setBounds(10, 30, 720, 310);
+
+                { // compute preferred size
+                    Dimension preferredSize = new Dimension();
+                    for (int i = 0; i < expressionPane.getComponentCount(); i++) {
+                        Rectangle bounds = expressionPane.getComponent(i).getBounds();
+                        preferredSize.width = Math.max(bounds.x + bounds.width, preferredSize.width);
+                        preferredSize.height = Math.max(bounds.y + bounds.height, preferredSize.height);
+                    }
+                    Insets insets = expressionPane.getInsets();
+                    preferredSize.width += insets.right;
+                    preferredSize.height += insets.bottom;
+                    expressionPane.setMinimumSize(preferredSize);
+                    expressionPane.setPreferredSize(preferredSize);
+                }
             }
-        });
-
-        org.jdesktop.layout.GroupLayout jPanel6Layout = new org.jdesktop.layout.GroupLayout(jPanel6);
-        jPanel6.setLayout(jPanel6Layout);
-        jPanel6Layout.setHorizontalGroup(
-                jPanel6Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel6Layout.createSequentialGroup()
-                        .add(jPanel6Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                .add(jPanel6Layout.createSequentialGroup()
+            tabbedPane.addTab("Probes", expressionPane);
+
+
+            //======== advancedPanel ========
+            {
+                advancedPanel.setBorder(new EmptyBorder(1, 10, 1, 10));
+                advancedPanel.setLayout(null);
+
+                //======== jPanel3 ========
+                {
+
+                    //======== jPanel2 ========
+                    {
+
+                        //---- jLabel1 ----
+                        jLabel1.setText("Genome Server URL");
+
+                        //---- genomeServerURLTextField ----
+                        genomeServerURLTextField.setText("jTextField1");
+                        genomeServerURLTextField.setEnabled(false);
+                        genomeServerURLTextField.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                genomeServerURLTextFieldActionPerformed(e);
+                            }
+                        });
+                        genomeServerURLTextField.addFocusListener(new FocusAdapter() {
+                            @Override
+                            public void focusLost(FocusEvent e) {
+                                genomeServerURLTextFieldFocusLost(e);
+                            }
+                        });
+
+                        //---- jLabel6 ----
+                        jLabel6.setText("Data Registry URL");
+
+                        //---- dataServerURLTextField ----
+                        dataServerURLTextField.setEnabled(false);
+                        dataServerURLTextField.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                dataServerURLTextFieldActionPerformed(e);
+                            }
+                        });
+                        dataServerURLTextField.addFocusListener(new FocusAdapter() {
+                            @Override
+                            public void focusLost(FocusEvent e) {
+                                dataServerURLTextFieldFocusLost(e);
+                            }
+                        });
+
+                        //---- editServerPropertiesCB ----
+                        editServerPropertiesCB.setText("Edit server properties");
+                        editServerPropertiesCB.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                editServerPropertiesCBActionPerformed(e);
+                            }
+                        });
+
+                        //---- clearGenomeCacheButton ----
+                        clearGenomeCacheButton.setText("Clear Genome Cache");
+                        clearGenomeCacheButton.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                clearGenomeCacheButtonActionPerformed(e);
+                            }
+                        });
+
+                        //---- jButton1 ----
+                        jButton1.setText("Reset to Defaults");
+                        jButton1.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                jButton1ActionPerformed(e);
+                            }
+                        });
+
+                        GroupLayout jPanel2Layout = new GroupLayout(jPanel2);
+                        jPanel2.setLayout(jPanel2Layout);
+                        jPanel2Layout.setHorizontalGroup(
+                                jPanel2Layout.createParallelGroup()
+                                        .add(jPanel2Layout.createSequentialGroup()
                                         .addContainerGap()
-                                        .add(jPanel6Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                        .add(jPanel6Layout.createSequentialGroup()
-                                                .add(44, 44, 44)
-                                                .add(missingDataExplanation3))
-                                        .add(jPanel6Layout.createSequentialGroup()
-                                                .add(jLabel23)
-                                                .add(18, 18, 18)
-                                                .add(expandCB)
-                                                .add(374, 374, 374))
-                                        .add(jPanel6Layout.createSequentialGroup()
-                                                .add(24, 24, 24)
-                                                .add(missingDataExplanation4, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 354, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                                        .add(jPanel6Layout.createSequentialGroup()
-                                                .add(trackNameAttributeLabel)
-                                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                                                .add(trackNameAttributeField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 216, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                                        .add(jPanel6Layout.createSequentialGroup()
-                                                .add(jLabel8)
-                                                .add(39, 39, 39)
-                                                .add(defaultTrackHeightField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 57, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                                        .add(jPanel6Layout.createSequentialGroup()
-                                                .add(24, 24, 24)
-                                                .add(missingDataExplanation5, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 634, Short.MAX_VALUE))
-                                        .add(jPanel6Layout.createSequentialGroup()
-                                        .add(jLabel5)
-                                        .add(36, 36, 36)
-                                        .add(defaultChartTrackHeightField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 57, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))))
-                                .add(jPanel6Layout.createSequentialGroup()
-                                .add(63, 63, 63)
-                                .add(missingDataExplanation2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 354, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)))
-                        .addContainerGap())
-        );
-        jPanel6Layout.setVerticalGroup(
-                jPanel6Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel6Layout.createSequentialGroup()
-                        .addContainerGap()
-                        .add(jPanel6Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
-                                .add(jLabel5)
-                                .add(defaultChartTrackHeightField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(missingDataExplanation4, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 25, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(jPanel6Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
-                                .add(jLabel8)
-                                .add(defaultTrackHeightField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(missingDataExplanation5, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 25, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                        .add(26, 26, 26)
-                        .add(jPanel6Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
-                                .add(trackNameAttributeLabel)
-                                .add(trackNameAttributeField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(missingDataExplanation2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 65, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                        .add(32, 32, 32)
-                        .add(jPanel6Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
-                                .add(jLabel23)
-                                .add(expandCB))
-                        .add(11, 11, 11)
-                        .add(missingDataExplanation3)
-                        .addContainerGap(82, Short.MAX_VALUE))
-        );
-
-        missingDataExplanation2.getAccessibleContext().setAccessibleName("<html>Name of an attribute to be used to label  tracks.  If provided   <br>tracks will be labeled with the corresponding attribute values from the sample information file. ");
-
-        tracksPanel.add(jPanel6);
-        jPanel6.setBounds(40, 20, 690, 440);
-
-        tabbedPane.addTab("Tracks", tracksPanel);
-
-        jLabel3.setText("Overlay tracks based on attribute:"); // NOI18N
-
-        overlayAttributeTextField.setText("LINKING_ID"); // NOI18N
-        overlayAttributeTextField.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                overlayAttributeTextFieldActionPerformed(evt);
-            }
-        });
-        overlayAttributeTextField.addFocusListener(new java.awt.event.FocusAdapter() {
-            public void focusLost(java.awt.event.FocusEvent evt) {
-                overlayAttributeTextFieldFocusLost(evt);
-            }
-        });
-        overlayAttributeTextField.addKeyListener(new java.awt.event.KeyAdapter() {
-            public void keyTyped(java.awt.event.KeyEvent evt) {
-                overlayAttributeTextFieldKeyTyped(evt);
-            }
-        });
-
-        overlayTrackCB.setSelected(true);
-        overlayTrackCB.setText("Overlay mutation tracks"); // NOI18N
-        overlayTrackCB.setActionCommand("overlayTracksCB"); // NOI18N
-        overlayTrackCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                overlayTrackCBActionPerformed(evt);
-            }
-        });
-
-        jLabel2.setFont(new java.awt.Font("Lucida Grande", 2, 12));
-        jLabel2.setText(getOverlayText());
-
-        displayTracksCB.setText("Display mutation data as distinct tracks"); // NOI18N
-        displayTracksCB.setActionCommand("displayTracksCB"); // NOI18N
-        displayTracksCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                displayTracksCBActionPerformed(evt);
-            }
-        });
-
-        jLabel4.setFont(new java.awt.Font("Lucida Grande", 2, 12));
-
-        colorOverlyCB.setText("Color code overlay"); // NOI18N
-        colorOverlyCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                colorOverlyCBActionPerformed(evt);
-            }
-        });
-
-        chooseOverlayColorsButton.setForeground(new java.awt.Color(0, 0, 247));
-        chooseOverlayColorsButton.setText("Choose colors"); // NOI18N
-        chooseOverlayColorsButton.setButtonStyle(
-                com.jidesoft.swing.ButtonStyle.HYPERLINK_STYLE
-        );
-        chooseOverlayColorsButton.setFont(new java.awt.Font("Lucida Grande", 2, 12));
-        chooseOverlayColorsButton.setVerticalAlignment(javax.swing.SwingConstants.BOTTOM);
-        chooseOverlayColorsButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
-        chooseOverlayColorsButton.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                chooseOverlayColorsButtonActionPerformed(evt);
-            }
-        });
-
-        org.jdesktop.layout.GroupLayout jPanel5Layout = new org.jdesktop.layout.GroupLayout(jPanel5);
-        jPanel5.setLayout(jPanel5Layout);
-        jPanel5Layout.setHorizontalGroup(
-                jPanel5Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel5Layout.createSequentialGroup()
-                        .addContainerGap()
-                        .add(jPanel5Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                .add(displayTracksCB)
-                                .add(jLabel4)
-                                .add(overlayTrackCB)
-                                .add(jLabel2)
-                                .add(jPanel5Layout.createSequentialGroup()
-                                .add(59, 59, 59)
-                                .add(jPanel5Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                .add(jPanel5Layout.createSequentialGroup()
-                                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                                        .add(colorOverlyCB)
-                                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                                        .add(chooseOverlayColorsButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                                .add(jPanel5Layout.createSequentialGroup()
-                                .add(jLabel3)
-                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                                .add(overlayAttributeTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 228, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)))))
-                        .addContainerGap())
-        );
-        jPanel5Layout.setVerticalGroup(
-                jPanel5Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel5Layout.createSequentialGroup()
-                        .addContainerGap()
-                        .add(jLabel2)
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(jLabel4)
-                        .add(39, 39, 39)
-                        .add(overlayTrackCB)
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(jPanel5Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
-                                .add(jLabel3)
-                                .add(overlayAttributeTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                        .add(6, 6, 6)
-                        .add(jPanel5Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
-                                .add(colorOverlyCB)
-                                .add(chooseOverlayColorsButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                        .add(36, 36, 36)
-                        .add(displayTracksCB)
-                        .addContainerGap(30, Short.MAX_VALUE))
-        );
-
-        org.jdesktop.layout.GroupLayout overlaysPanelLayout = new org.jdesktop.layout.GroupLayout(overlaysPanel);
-        overlaysPanel.setLayout(overlaysPanelLayout);
-        overlaysPanelLayout.setHorizontalGroup(
-                overlaysPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(overlaysPanelLayout.createSequentialGroup()
-                        .add(28, 28, 28)
-                        .add(jPanel5, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                        .addContainerGap(196, Short.MAX_VALUE))
-        );
-        overlaysPanelLayout.setVerticalGroup(
-                overlaysPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(overlaysPanelLayout.createSequentialGroup()
-                        .add(55, 55, 55)
-                        .add(jPanel5, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                        .addContainerGap(214, Short.MAX_VALUE))
-        );
-
-        tabbedPane.addTab("Overlays", overlaysPanel);
-
-        chartPanel.setLayout(null);
-
-        topBorderCB.setText("Draw Top Border");
-        topBorderCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                topBorderCBActionPerformed(evt);
-            }
-        });
-
-        label1.setFont(label1.getFont());
-        label1.setText("Settings for barcharts and scatterplots:");
-
-        chartDrawTrackNameCB.setText("Draw Track Label");
-        chartDrawTrackNameCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                chartDrawTrackNameCBActionPerformed(evt);
-            }
-        });
-
-        bottomBorderCB.setText("Draw Bottom Border");
-        bottomBorderCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                bottomBorderCBActionPerformed(evt);
-            }
-        });
-
-        jLabel7.setText("<html><i>Draw a label centered over the track provided<br>the track height is at least 25 pixels. ");
-
-        colorBordersCB.setText("Color Borders");
-        colorBordersCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                colorBordersCBActionPerformed(evt);
-            }
-        });
-
-        labelYAxisCB.setText("Label Y Axis");
-        labelYAxisCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                labelYAxisCBActionPerformed(evt);
-            }
-        });
-
-        org.jdesktop.layout.GroupLayout jPanel4Layout = new org.jdesktop.layout.GroupLayout(jPanel4);
-        jPanel4.setLayout(jPanel4Layout);
-        jPanel4Layout.setHorizontalGroup(
-                jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel4Layout.createSequentialGroup()
-                        .addContainerGap()
-                        .add(jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                .add(label1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                .add(jPanel4Layout.createSequentialGroup()
-                                .add(20, 20, 20)
-                                .add(jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                .add(jPanel4Layout.createSequentialGroup()
-                                        .add(chartDrawTrackNameCB)
-                                        .add(61, 61, 61)
-                                        .add(jLabel7))
-                                .add(topBorderCB)
-                                .add(colorBordersCB)
-                                .add(bottomBorderCB)
-                                .add(labelYAxisCB))))
-                        .addContainerGap())
-        );
-        jPanel4Layout.setVerticalGroup(
-                jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel4Layout.createSequentialGroup()
-                        .addContainerGap()
-                        .add(label1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(topBorderCB)
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(bottomBorderCB)
-                        .add(7, 7, 7)
-                        .add(colorBordersCB)
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                .add(jLabel7)
-                                .add(chartDrawTrackNameCB))
-                        .add(32, 32, 32)
-                        .add(labelYAxisCB)
-                        .add(122, 122, 122))
-        );
-
-        chartPanel.add(jPanel4);
-        jPanel4.setBounds(20, 30, 580, 330);
-
-        tabbedPane.addTab("Charts", chartPanel);
-
-        alignmentPanel.setLayout(null);
-
-        samMaxWindowSizeField.setText("jTextField1");
-        samMaxWindowSizeField.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                samMaxWindowSizeFieldActionPerformed(evt);
-            }
-        });
-        samMaxWindowSizeField.addFocusListener(new java.awt.event.FocusAdapter() {
-            public void focusLost(java.awt.event.FocusEvent evt) {
-                samMaxWindowSizeFieldFocusLost(evt);
-            }
-        });
-
-        jLabel11.setText("Visibility range threshold (kb)");
-
-        samShowDuplicatesCB.setText("Show duplicate reads");
-        samShowDuplicatesCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                samShowDuplicatesCBActionPerformed(evt);
-            }
-        });
-
-        jLabel12.setText("<html><i>Nominal window size at which alignments become visible");
-
-        samFlagUnmappedPairCB.setText("Flag unmapped pairs");
-        samFlagUnmappedPairCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                samFlagUnmappedPairCBActionPerformed(evt);
-            }
-        });
-
-        jLabel13.setText("Maximum read depth");
-
-        samMaxLevelField.setText("jTextField1");
-        samMaxLevelField.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                samMaxLevelFieldActionPerformed(evt);
-            }
-        });
-        samMaxLevelField.addFocusListener(new java.awt.event.FocusAdapter() {
-            public void focusLost(java.awt.event.FocusEvent evt) {
-                samMaxLevelFieldFocusLost(evt);
-            }
-        });
-
-        jLabel14.setText("<html><i>Maximum number of rows of alignments to display.");
-
-        jLabel15.setText("Mapping quality threshold:");
-
-        mappingQualityThresholdField.setText("0");
-        mappingQualityThresholdField.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                mappingQualityThresholdFieldActionPerformed(evt);
-            }
-        });
-        mappingQualityThresholdField.addFocusListener(new java.awt.event.FocusAdapter() {
-            public void focusLost(java.awt.event.FocusEvent evt) {
-                mappingQualityThresholdFieldFocusLost(evt);
-            }
-        });
-
-        jLabel16.setText("<html><i>Reads with qualities  below the threshold are not shown.");
-
-        jLabel17.setText("Insert size flagging threshold:");
-
-        insertSizeThresholdField.setText("0");
-        insertSizeThresholdField.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                insertSizeThresholdFieldActionPerformed(evt);
-            }
-        });
-        insertSizeThresholdField.addFocusListener(new java.awt.event.FocusAdapter() {
-            public void focusLost(java.awt.event.FocusEvent evt) {
-                insertSizeThresholdFieldFocusLost(evt);
-            }
-        });
-
-        jLabel18.setText("<html><i>Paired end alignments with insert sizes > this value are flagged.");
-
-        jLabel10.setText("<html>Chromosome color legend &nbsp;&nbsp;&nbsp;<i>Used to flag paired end reads with mates on other chromosomes");
-
-        org.jdesktop.layout.GroupLayout legendPanelLayout = new org.jdesktop.layout.GroupLayout(legendPanel);
-        legendPanel.setLayout(legendPanelLayout);
-        legendPanelLayout.setHorizontalGroup(
-                legendPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(0, 723, Short.MAX_VALUE)
-        );
-        legendPanelLayout.setVerticalGroup(
-                legendPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(0, 57, Short.MAX_VALUE)
-        );
-
-        showRefSeqCB.setText("Show reference sequence");
-        showRefSeqCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                showRefSeqCBActionPerformed(evt);
-            }
-        });
-
-        shadeCenterCB.setText("Shade alignments intersecting center");
-        shadeCenterCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                shadeCenterCBActionPerformed(evt);
-            }
-        });
-
-        showCovTrackCB.setText("Show coverage track");
-        showCovTrackCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                showCovTrackCBActionPerformed(evt);
-            }
-        });
-
-        samShadeMismatchedBaseCB.setText("Shade mismatched bases by quality. ");
-        samShadeMismatchedBaseCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                samShadeMismatchedBaseCBActionPerformed(evt);
-            }
-        });
-
-        samMinBaseQualityField.setText("0");
-        samMinBaseQualityField.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                samMinBaseQualityFieldActionPerformed(evt);
-            }
-        });
-        samMinBaseQualityField.addFocusListener(new java.awt.event.FocusAdapter() {
-            public void focusLost(java.awt.event.FocusEvent evt) {
-                samMinBaseQualityFieldFocusLost(evt);
-            }
-        });
-
-        samMaxBaseQualityField.setText("0");
-        samMaxBaseQualityField.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                samMaxBaseQualityFieldActionPerformed(evt);
-            }
-        });
-        samMaxBaseQualityField.addFocusListener(new java.awt.event.FocusAdapter() {
-            public void focusLost(java.awt.event.FocusEvent evt) {
-                samMaxBaseQualityFieldFocusLost(evt);
-            }
-        });
-
-        jLabel19.setText("Min: ");
-
-        jLabel20.setText("Max:");
-
-        filterCB.setText("Filter alignments");
-        filterCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                filterCBActionPerformed(evt);
-            }
-        });
-
-        filterURL.setText("URL or path to filter file");
-        filterURL.setEnabled(false);
-        filterURL.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                filterURLActionPerformed(evt);
-            }
-        });
-        filterURL.addFocusListener(new java.awt.event.FocusAdapter() {
-            public void focusLost(java.awt.event.FocusEvent evt) {
-                filterURLFocusLost(evt);
-            }
-        });
-
-        org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1);
-        jPanel1.setLayout(jPanel1Layout);
-        jPanel1Layout.setHorizontalGroup(
-                jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel1Layout.createSequentialGroup()
-                        .addContainerGap()
-                        .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel1Layout.createSequentialGroup()
-                                .add(legendPanel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                .addContainerGap())
-                        .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel1Layout.createSequentialGroup()
-                                .add(samShadeMismatchedBaseCB, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 290, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                .add(32, 32, 32)
-                                .add(jLabel19)
-                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
-                                .add(samMinBaseQualityField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 50, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                .add(31, 31, 31)
-                                .add(jLabel20)
-                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                                .add(samMaxBaseQualityField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 50, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                .addContainerGap())
-                        .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel1Layout.createSequentialGroup()
-                                .add(showCovTrackCB, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 270, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                .addContainerGap())
-                        .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel1Layout.createSequentialGroup()
-                                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                        .add(jPanel1Layout.createSequentialGroup()
-                                                .add(jLabel11)
-                                                .add(14, 14, 14)
-                                                .add(samMaxWindowSizeField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                                .add(16, 16, 16)
-                                                .add(jLabel12))
-                                        .add(jLabel10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 640, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                        .add(jPanel1Layout.createSequentialGroup()
-                                                .add(jLabel13)
-                                                .add(67, 67, 67)
-                                                .add(samMaxLevelField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                                .add(16, 16, 16)
-                                                .add(jLabel14, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 390, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                                        .add(jPanel1Layout.createSequentialGroup()
-                                        .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                                .add(jPanel1Layout.createSequentialGroup()
-                                                        .add(jLabel15)
-                                                        .add(31, 31, 31)
-                                                        .add(mappingQualityThresholdField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 70, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                                                .add(jPanel1Layout.createSequentialGroup()
-                                                .add(jLabel17, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 200, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                                .add(insertSizeThresholdField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 70, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)))
-                                        .add(30, 30, 30)
-                                        .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                        .add(jLabel18, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 360, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                        .add(jLabel16))))
-                                .addContainerGap(30, Short.MAX_VALUE))
-                        .add(jPanel1Layout.createSequentialGroup()
-                                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
-                                        .add(jPanel1Layout.createSequentialGroup()
-                                                .add(samShowDuplicatesCB, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 290, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 20, Short.MAX_VALUE))
-                                        .add(jPanel1Layout.createSequentialGroup()
-                                        .add(samFlagUnmappedPairCB, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 310, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)))
-                                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                        .add(showRefSeqCB, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 270, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                        .add(shadeCenterCB, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 450, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                                .add(3, 3, 3))
-                        .add(jPanel1Layout.createSequentialGroup()
-                        .add(filterCB, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 144, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                        .add(27, 27, 27)
-                        .add(filterURL, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 482, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                        .addContainerGap()))))))
-        );
-        jPanel1Layout.setVerticalGroup(
-                jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel1Layout.createSequentialGroup()
-                        .addContainerGap()
-                        .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.CENTER)
-                                .add(jLabel11)
-                                .add(samMaxWindowSizeField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                .add(jLabel12))
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.CENTER)
-                                .add(jLabel13)
-                                .add(samMaxLevelField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                .add(jLabel14, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 30, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.CENTER)
-                                .add(jLabel15)
-                                .add(mappingQualityThresholdField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                .add(jLabel16))
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.CENTER)
-                                .add(jLabel17)
-                                .add(insertSizeThresholdField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                .add(jLabel18, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 40, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                        .add(18, 18, 18)
-                        .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
-                                .add(jPanel1Layout.createSequentialGroup()
-                                        .add(samShowDuplicatesCB)
-                                        .add(30, 30, 30))
-                                .add(jPanel1Layout.createSequentialGroup()
-                                .add(showRefSeqCB)
-                                .add(7, 7, 7)
-                                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
-                                .add(shadeCenterCB)
-                                .add(samFlagUnmappedPairCB))))
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
-                        .add(showCovTrackCB)
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
-                        .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.CENTER)
-                                .add(samShadeMismatchedBaseCB)
-                                .add(samMinBaseQualityField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                .add(samMaxBaseQualityField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                .add(jLabel19)
-                                .add(jLabel20))
-                        .add(22, 22, 22)
-                        .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
-                                .add(filterCB)
-                                .add(filterURL, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                        .add(29, 29, 29)
-                        .add(jLabel10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 30, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
-                        .add(legendPanel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
-                        .addContainerGap())
-        );
-
-        alignmentPanel.add(jPanel1);
-        jPanel1.setBounds(20, 0, 740, 510);
-
-        tabbedPane.addTab("Alignments", alignmentPanel);
-
-        expressionPane.setLayout(null);
-
-        probeMappingButtonGroup.add(expMapToGeneCB);
-        expMapToGeneCB.setText("Map probes to genes");
-        expMapToGeneCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                expMapToGeneCBActionPerformed(evt);
-            }
-        });
-
-        jLabel24.setText("Expression probe mapping options: ");
-
-        probeMappingButtonGroup.add(expMapToLociCB);
-        expMapToLociCB.setText("<html>Map probes to target loci");
-        expMapToLociCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                expMapToLociCBActionPerformed(evt);
-            }
-        });
-
-        jLabel21.setText("<html><i>Note: Changes will not affect currently loaded datasets.");
-
-        org.jdesktop.layout.GroupLayout jPanel8Layout = new org.jdesktop.layout.GroupLayout(jPanel8);
-        jPanel8.setLayout(jPanel8Layout);
-        jPanel8Layout.setHorizontalGroup(
-                jPanel8Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel8Layout.createSequentialGroup()
-                        .add(jPanel8Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                .add(jPanel8Layout.createSequentialGroup()
-                                        .add(45, 45, 45)
-                                        .add(jPanel8Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                        .add(expMapToLociCB)
-                                        .add(expMapToGeneCB)))
-                                .add(jPanel8Layout.createSequentialGroup()
-                                .addContainerGap()
-                                .add(jPanel8Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                .add(jPanel8Layout.createSequentialGroup()
-                                        .add(24, 24, 24)
-                                        .add(jLabel21, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 497, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                                .add(jLabel24))))
-                        .addContainerGap(179, Short.MAX_VALUE))
-        );
-        jPanel8Layout.setVerticalGroup(
-                jPanel8Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel8Layout.createSequentialGroup()
-                        .addContainerGap()
-                        .add(jLabel24)
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(jLabel21, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 44, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(expMapToLociCB)
-                        .add(14, 14, 14)
-                        .add(expMapToGeneCB)
-                        .addContainerGap(158, Short.MAX_VALUE))
-        );
-
-        expressionPane.add(jPanel8);
-        jPanel8.setBounds(10, 30, 720, 310);
-
-        tabbedPane.addTab("Probes", expressionPane);
-
-        advancedPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 10, 1, 10));
-        advancedPanel.setLayout(null);
-
-        jLabel1.setText("Genome Server URL");
-
-        genomeServerURLTextField.setText("jTextField1");
-        genomeServerURLTextField.setEnabled(false);
-        genomeServerURLTextField.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                genomeServerURLTextFieldActionPerformed(evt);
-            }
-        });
-        genomeServerURLTextField.addFocusListener(new java.awt.event.FocusAdapter() {
-            public void focusLost(java.awt.event.FocusEvent evt) {
-                genomeServerURLTextFieldFocusLost(evt);
-            }
-        });
-
-        jLabel6.setText("Data Registry URL");
-
-        dataServerURLTextField.setEnabled(false);
-        dataServerURLTextField.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                dataServerURLTextFieldActionPerformed(evt);
-            }
-        });
-        dataServerURLTextField.addFocusListener(new java.awt.event.FocusAdapter() {
-            public void focusLost(java.awt.event.FocusEvent evt) {
-                dataServerURLTextFieldFocusLost(evt);
-            }
-        });
-
-        editServerPropertiesCB.setText("Edit server properties");
-        editServerPropertiesCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                editServerPropertiesCBActionPerformed(evt);
-            }
-        });
-
-        clearGenomeCacheButton.setText("Clear Genome Cache");
-        clearGenomeCacheButton.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                clearGenomeCacheButtonActionPerformed(evt);
-            }
-        });
-
-        jButton1.setText("Reset to Defaults");
-        jButton1.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                jButton1ActionPerformed(evt);
-            }
-        });
-
-        org.jdesktop.layout.GroupLayout jPanel2Layout = new org.jdesktop.layout.GroupLayout(jPanel2);
-        jPanel2.setLayout(jPanel2Layout);
-        jPanel2Layout.setHorizontalGroup(
-                jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel2Layout.createSequentialGroup()
-                        .addContainerGap()
-                        .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                .add(jPanel2Layout.createSequentialGroup()
-                                        .add(29, 29, 29)
-                                        .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                        .add(jLabel1)
+                                        .add(jPanel2Layout.createParallelGroup()
+                                                .add(jPanel2Layout.createSequentialGroup()
+                                                        .add(29, 29, 29)
+                                                        .add(jPanel2Layout.createParallelGroup()
+                                                        .add(jLabel1)
+                                                        .add(jPanel2Layout.createSequentialGroup()
+                                                        .add(jLabel6)
+                                                        .add(44, 44, 44)
+                                                        .add(jPanel2Layout.createParallelGroup(GroupLayout.LEADING, false)
+                                                        .add(dataServerURLTextField)
+                                                        .add(genomeServerURLTextField, GroupLayout.PREFERRED_SIZE, 494, GroupLayout.PREFERRED_SIZE)))))
+                                                .add(jPanel2Layout.createSequentialGroup()
+                                                        .add(editServerPropertiesCB)
+                                                        .add(18, 18, 18)
+                                                        .add(jButton1))
+                                                .add(clearGenomeCacheButton))
+                                        .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                        );
+                        jPanel2Layout.setVerticalGroup(
+                                jPanel2Layout.createParallelGroup()
                                         .add(jPanel2Layout.createSequentialGroup()
-                                        .add(jLabel6)
-                                        .add(44, 44, 44)
-                                        .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING, false)
-                                        .add(dataServerURLTextField)
-                                        .add(genomeServerURLTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 494, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)))))
-                                .add(jPanel2Layout.createSequentialGroup()
-                                        .add(editServerPropertiesCB)
+                                        .addContainerGap()
+                                        .add(jPanel2Layout.createParallelGroup(GroupLayout.BASELINE)
+                                                .add(editServerPropertiesCB)
+                                                .add(jButton1))
+                                        .addPreferredGap(LayoutStyle.RELATED)
+                                        .add(jPanel2Layout.createParallelGroup(GroupLayout.CENTER)
+                                                .add(jLabel1)
+                                                .add(genomeServerURLTextField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                                        .addPreferredGap(LayoutStyle.RELATED)
+                                        .add(jPanel2Layout.createParallelGroup(GroupLayout.BASELINE)
+                                                .add(jLabel6)
+                                                .add(dataServerURLTextField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
                                         .add(18, 18, 18)
-                                        .add(jButton1))
-                                .add(clearGenomeCacheButton))
-                        .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
-        );
-        jPanel2Layout.setVerticalGroup(
-                jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel2Layout.createSequentialGroup()
-                        .addContainerGap()
-                        .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
-                                .add(editServerPropertiesCB)
-                                .add(jButton1))
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.CENTER)
-                                .add(jLabel1)
-                                .add(genomeServerURLTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
-                                .add(jLabel6)
-                                .add(dataServerURLTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                        .add(18, 18, 18)
-                        .add(clearGenomeCacheButton)
-                        .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
-        );
-
-        enablePortCB.setText("Enable port");
-        enablePortCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                enablePortCBActionPerformed(evt);
-            }
-        });
-
-        portField.setText("60151");
-        portField.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                portFieldActionPerformed(evt);
-            }
-        });
-        portField.addFocusListener(new java.awt.event.FocusAdapter() {
-            public void focusLost(java.awt.event.FocusEvent evt) {
-                portFieldFocusLost(evt);
-            }
-        });
+                                        .add(clearGenomeCacheButton)
+                                        .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                        );
+                    }
 
-        jLabel22.setFont(new java.awt.Font("Lucida Grande", 2, 13));
-        jLabel22.setText("Enable port to send commands and http requests to IGV. ");
+                    //======== jPanel7 ========
+                    {
+
+                        //---- enablePortCB ----
+                        enablePortCB.setText("Enable port");
+                        enablePortCB.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                enablePortCBActionPerformed(e);
+                            }
+                        });
+
+                        //---- portField ----
+                        portField.setText("60151");
+                        portField.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                portFieldActionPerformed(e);
+                            }
+                        });
+                        portField.addFocusListener(new FocusAdapter() {
+                            @Override
+                            public void focusLost(FocusEvent e) {
+                                portFieldFocusLost(e);
+                            }
+                        });
+
+                        //---- jLabel22 ----
+                        jLabel22.setFont(new Font("Lucida Grande", Font.ITALIC, 13));
+                        jLabel22.setText("Enable port to send commands and http requests to IGV. ");
+
+                        GroupLayout jPanel7Layout = new GroupLayout(jPanel7);
+                        jPanel7.setLayout(jPanel7Layout);
+                        jPanel7Layout.setHorizontalGroup(
+                                jPanel7Layout.createParallelGroup()
+                                        .add(jPanel7Layout.createSequentialGroup()
+                                        .add(jPanel7Layout.createParallelGroup()
+                                                .add(jPanel7Layout.createSequentialGroup()
+                                                        .addContainerGap()
+                                                        .add(enablePortCB)
+                                                        .add(39, 39, 39)
+                                                        .add(portField, GroupLayout.PREFERRED_SIZE, 126, GroupLayout.PREFERRED_SIZE))
+                                                .add(jPanel7Layout.createSequentialGroup()
+                                                .add(48, 48, 48)
+                                                .add(jLabel22)))
+                                        .addContainerGap(330, Short.MAX_VALUE))
+                        );
+                        jPanel7Layout.setVerticalGroup(
+                                jPanel7Layout.createParallelGroup()
+                                        .add(jPanel7Layout.createSequentialGroup()
+                                        .add(28, 28, 28)
+                                        .add(jPanel7Layout.createParallelGroup(GroupLayout.CENTER)
+                                                .add(enablePortCB)
+                                                .add(portField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                                        .addPreferredGap(LayoutStyle.UNRELATED)
+                                        .add(jLabel22)
+                                        .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                        );
+                    }
 
-        org.jdesktop.layout.GroupLayout jPanel7Layout = new org.jdesktop.layout.GroupLayout(jPanel7);
-        jPanel7.setLayout(jPanel7Layout);
-        jPanel7Layout.setHorizontalGroup(
-                jPanel7Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel7Layout.createSequentialGroup()
-                        .add(jPanel7Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                .add(jPanel7Layout.createSequentialGroup()
-                                        .addContainerGap()
-                                        .add(enablePortCB)
-                                        .add(39, 39, 39)
-                                        .add(portField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 126, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                                .add(jPanel7Layout.createSequentialGroup()
-                                .add(48, 48, 48)
-                                .add(jLabel22)))
-                        .addContainerGap(314, Short.MAX_VALUE))
-        );
-        jPanel7Layout.setVerticalGroup(
-                jPanel7Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel7Layout.createSequentialGroup()
-                        .add(28, 28, 28)
-                        .add(jPanel7Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.CENTER)
-                                .add(enablePortCB)
-                                .add(portField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
-                        .add(jLabel22)
-                        .addContainerGap(48, Short.MAX_VALUE))
-        );
-
-        org.jdesktop.layout.GroupLayout jPanel3Layout = new org.jdesktop.layout.GroupLayout(jPanel3);
-        jPanel3.setLayout(jPanel3Layout);
-        jPanel3Layout.setHorizontalGroup(
-                jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel3Layout.createSequentialGroup()
-                        .addContainerGap()
-                        .add(jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(org.jdesktop.layout.GroupLayout.TRAILING, jPanel3Layout.createSequentialGroup()
-                                .add(jPanel2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                .add(14, 14, 14))
-                        .add(jPanel3Layout.createSequentialGroup()
-                        .add(jPanel7, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
-                        .add(8, 8, 8))))
-        );
-        jPanel3Layout.setVerticalGroup(
-                jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel3Layout.createSequentialGroup()
-                        .add(20, 20, 20)
-                        .add(jPanel7, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
-                        .add(jPanel2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                        .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
-        );
-
-        advancedPanel.add(jPanel3);
-        jPanel3.setBounds(10, 0, 750, 350);
-
-        useByteRangeCB.setText("Use http byte-range requests");
-        useByteRangeCB.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                useByteRangeCBActionPerformed(evt);
+                    GroupLayout jPanel3Layout = new GroupLayout(jPanel3);
+                    jPanel3.setLayout(jPanel3Layout);
+                    jPanel3Layout.setHorizontalGroup(
+                            jPanel3Layout.createParallelGroup()
+                                    .add(jPanel3Layout.createSequentialGroup()
+                                    .addContainerGap()
+                                    .add(jPanel3Layout.createParallelGroup()
+                                            .add(jPanel7, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                            .add(jPanel2, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                                    .addContainerGap())
+                    );
+                    jPanel3Layout.setVerticalGroup(
+                            jPanel3Layout.createParallelGroup()
+                                    .add(jPanel3Layout.createSequentialGroup()
+                                    .add(20, 20, 20)
+                                    .add(jPanel7, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                                    .addPreferredGap(LayoutStyle.RELATED, 58, Short.MAX_VALUE)
+                                    .add(jPanel2, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                                    .addContainerGap())
+                    );
+                }
+                advancedPanel.add(jPanel3);
+                jPanel3.setBounds(10, 0, 750, 330);
+
+                //======== jPanel9 ========
+                {
+
+                    //---- useByteRangeCB ----
+                    useByteRangeCB.setText("Use http byte-range requests");
+                    useByteRangeCB.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            useByteRangeCBActionPerformed(e);
+                        }
+                    });
+
+                    //---- jLabel25 ----
+                    jLabel25.setFont(new Font("Lucida Grande", Font.ITALIC, 13));
+                    jLabel25.setText("<html>This option applies to certain \"Load from Server...\" tracks hosted at the Broad.    Disable this option if you are unable to load the phastCons conservation track under the hg18 annotations.");
+
+                    GroupLayout jPanel9Layout = new GroupLayout(jPanel9);
+                    jPanel9.setLayout(jPanel9Layout);
+                    jPanel9Layout.setHorizontalGroup(
+                            jPanel9Layout.createParallelGroup()
+                                    .add(jPanel9Layout.createSequentialGroup()
+                                    .add(jPanel9Layout.createParallelGroup()
+                                            .add(jPanel9Layout.createSequentialGroup()
+                                                    .add(44, 44, 44)
+                                                    .add(jLabel25, GroupLayout.PREFERRED_SIZE, 601, GroupLayout.PREFERRED_SIZE))
+                                            .add(jPanel9Layout.createSequentialGroup()
+                                            .addContainerGap()
+                                            .add(useByteRangeCB)))
+                                    .addContainerGap(65, Short.MAX_VALUE))
+                    );
+                    jPanel9Layout.setVerticalGroup(
+                            jPanel9Layout.createParallelGroup()
+                                    .add(jPanel9Layout.createSequentialGroup()
+                                    .addContainerGap()
+                                    .add(useByteRangeCB, GroupLayout.PREFERRED_SIZE, 38, GroupLayout.PREFERRED_SIZE)
+                                    .addPreferredGap(LayoutStyle.RELATED)
+                                    .add(jLabel25, GroupLayout.DEFAULT_SIZE, 64, Short.MAX_VALUE)
+                                    .add(6, 6, 6))
+                    );
+                }
+                advancedPanel.add(jPanel9);
+                jPanel9.setBounds(30, 340, 710, 120);
+
+                { // compute preferred size
+                    Dimension preferredSize = new Dimension();
+                    for (int i = 0; i < advancedPanel.getComponentCount(); i++) {
+                        Rectangle bounds = advancedPanel.getComponent(i).getBounds();
+                        preferredSize.width = Math.max(bounds.x + bounds.width, preferredSize.width);
+                        preferredSize.height = Math.max(bounds.y + bounds.height, preferredSize.height);
+                    }
+                    Insets insets = advancedPanel.getInsets();
+                    preferredSize.width += insets.right;
+                    preferredSize.height += insets.bottom;
+                    advancedPanel.setMinimumSize(preferredSize);
+                    advancedPanel.setPreferredSize(preferredSize);
+                }
             }
-        });
+            tabbedPane.addTab("Advanced", advancedPanel);
+
+
+            //======== proxyPanel ========
+            {
+                proxyPanel.setLayout(new BoxLayout(proxyPanel, BoxLayout.X_AXIS));
+
+                //======== jPanel15 ========
+                {
+
+                    //======== jPanel16 ========
+                    {
+
+                        //---- proxyUsernameField ----
+                        proxyUsernameField.setText("jTextField1");
+                        proxyUsernameField.setEnabled(false);
+                        proxyUsernameField.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                proxyUsernameFieldActionPerformed(e);
+                            }
+                        });
+                        proxyUsernameField.addFocusListener(new FocusAdapter() {
+                            @Override
+                            public void focusLost(FocusEvent e) {
+                                proxyUsernameFieldFocusLost(e);
+                            }
+                        });
+
+                        //---- jLabel28 ----
+                        jLabel28.setText("Username");
+
+                        //---- authenticateProxyCB ----
+                        authenticateProxyCB.setText("Authentication required");
+                        authenticateProxyCB.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                authenticateProxyCBActionPerformed(e);
+                            }
+                        });
+
+                        //---- jLabel29 ----
+                        jLabel29.setText("Password");
+
+                        //---- proxyPasswordField ----
+                        proxyPasswordField.setText("jPasswordField1");
+                        proxyPasswordField.addFocusListener(new FocusAdapter() {
+                            @Override
+                            public void focusLost(FocusEvent e) {
+                                proxyPasswordFieldFocusLost(e);
+                            }
+                        });
+
+                        GroupLayout jPanel16Layout = new GroupLayout(jPanel16);
+                        jPanel16.setLayout(jPanel16Layout);
+                        jPanel16Layout.setHorizontalGroup(
+                                jPanel16Layout.createParallelGroup()
+                                        .add(jPanel16Layout.createSequentialGroup()
+                                        .add(jPanel16Layout.createParallelGroup()
+                                                .add(jPanel16Layout.createSequentialGroup()
+                                                        .add(28, 28, 28)
+                                                        .add(jPanel16Layout.createParallelGroup()
+                                                                .add(jLabel28)
+                                                                .add(jLabel29))
+                                                        .add(37, 37, 37)
+                                                        .add(jPanel16Layout.createParallelGroup(GroupLayout.LEADING, false)
+                                                        .add(proxyPasswordField)
+                                                        .add(proxyUsernameField, GroupLayout.DEFAULT_SIZE, 261, Short.MAX_VALUE)))
+                                                .add(jPanel16Layout.createSequentialGroup()
+                                                .addContainerGap()
+                                                .add(authenticateProxyCB)))
+                                        .addContainerGap(354, Short.MAX_VALUE))
+                        );
+                        jPanel16Layout.setVerticalGroup(
+                                jPanel16Layout.createParallelGroup()
+                                        .add(jPanel16Layout.createSequentialGroup()
+                                        .add(17, 17, 17)
+                                        .add(authenticateProxyCB)
+                                        .addPreferredGap(LayoutStyle.RELATED)
+                                        .add(jPanel16Layout.createParallelGroup(GroupLayout.BASELINE)
+                                                .add(jLabel28)
+                                                .add(proxyUsernameField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                                        .addPreferredGap(LayoutStyle.RELATED)
+                                        .add(jPanel16Layout.createParallelGroup(GroupLayout.BASELINE)
+                                                .add(jLabel29)
+                                                .add(proxyPasswordField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                                        .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                        );
+                    }
 
-        jLabel25.setFont(new java.awt.Font("Lucida Grande", 2, 13)); // NOI18N
-        jLabel25.setText("<html>This option applies to certain \"Load from Server...\" tracks hosted at the Broad.    Disable if you are unable to load the phastCons conservation track under the hg18 annotations.");
-
-        org.jdesktop.layout.GroupLayout jPanel9Layout = new org.jdesktop.layout.GroupLayout(jPanel9);
-        jPanel9.setLayout(jPanel9Layout);
-        jPanel9Layout.setHorizontalGroup(
-                jPanel9Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel9Layout.createSequentialGroup()
-                        .add(jPanel9Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                .add(jPanel9Layout.createSequentialGroup()
-                                        .add(44, 44, 44)
-                                        .add(jLabel25, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 601, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
-                                .add(jPanel9Layout.createSequentialGroup()
-                                .addContainerGap()
-                                .add(useByteRangeCB)))
-                        .addContainerGap(65, Short.MAX_VALUE))
-        );
-        jPanel9Layout.setVerticalGroup(
-                jPanel9Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(jPanel9Layout.createSequentialGroup()
-                        .addContainerGap()
-                        .add(useByteRangeCB, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 38, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
-                        .add(jLabel25, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 61, Short.MAX_VALUE)
-                        .add(6, 6, 6))
-        );
-
-        advancedPanel.add(jPanel9);
-        jPanel9.setBounds(30, 370, 710, 120);
-
-        tabbedPane.addTab("Advanced", advancedPanel);
-
-        getContentPane().add(tabbedPane, java.awt.BorderLayout.LINE_END);
-        tabbedPane.getAccessibleContext().setAccessibleName("Advanced");
-
-        okButton.setText("OK"); // NOI18N
-        okButton.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                okButtonActionPerformed(evt);
-            }
-        });
-        okCancelButtonPanel.add(okButton);
+                    //======== jPanel17 ========
+                    {
+
+                        //---- proxyHostField ----
+                        proxyHostField.setText("jTextField1");
+                        proxyHostField.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                proxyHostFieldActionPerformed(e);
+                            }
+                        });
+                        proxyHostField.addFocusListener(new FocusAdapter() {
+                            @Override
+                            public void focusLost(FocusEvent e) {
+                                proxyHostFieldFocusLost(e);
+                            }
+                        });
+
+                        //---- proxyPortField ----
+                        proxyPortField.setText("jTextField1");
+                        proxyPortField.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                proxyPortFieldActionPerformed(e);
+                            }
+                        });
+                        proxyPortField.addFocusListener(new FocusAdapter() {
+                            @Override
+                            public void focusLost(FocusEvent e) {
+                                proxyPortFieldFocusLost(e);
+                            }
+                        });
+
+                        //---- jLabel27 ----
+                        jLabel27.setText("Proxy port");
+
+                        //---- jLabel23 ----
+                        jLabel23.setText("Proxy host");
+
+                        //---- useProxyCB ----
+                        useProxyCB.setText("Use proxy");
+                        useProxyCB.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                useProxyCBActionPerformed(e);
+                            }
+                        });
+
+                        GroupLayout jPanel17Layout = new GroupLayout(jPanel17);
+                        jPanel17.setLayout(jPanel17Layout);
+                        jPanel17Layout.setHorizontalGroup(
+                                jPanel17Layout.createParallelGroup()
+                                        .add(jPanel17Layout.createSequentialGroup()
+                                        .add(jPanel17Layout.createParallelGroup()
+                                                .add(jPanel17Layout.createSequentialGroup()
+                                                        .addContainerGap()
+                                                        .add(jPanel17Layout.createParallelGroup()
+                                                                .add(jLabel27)
+                                                                .add(jLabel23))
+                                                        .add(28, 28, 28)
+                                                        .add(jPanel17Layout.createParallelGroup()
+                                                        .add(proxyPortField, GroupLayout.PREFERRED_SIZE, 108, GroupLayout.PREFERRED_SIZE)
+                                                        .add(proxyHostField, GroupLayout.PREFERRED_SIZE, 485, GroupLayout.PREFERRED_SIZE)))
+                                                .add(jPanel17Layout.createSequentialGroup()
+                                                .add(9, 9, 9)
+                                                .add(useProxyCB)))
+                                        .addContainerGap(21, Short.MAX_VALUE))
+                        );
+                        jPanel17Layout.setVerticalGroup(
+                                jPanel17Layout.createParallelGroup()
+                                        .add(GroupLayout.TRAILING, jPanel17Layout.createSequentialGroup()
+                                        .addContainerGap(29, Short.MAX_VALUE)
+                                        .add(useProxyCB)
+                                        .add(18, 18, 18)
+                                        .add(jPanel17Layout.createParallelGroup(GroupLayout.BASELINE)
+                                                .add(jLabel23)
+                                                .add(proxyHostField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                                        .addPreferredGap(LayoutStyle.RELATED)
+                                        .add(jPanel17Layout.createParallelGroup(GroupLayout.BASELINE)
+                                                .add(jLabel27)
+                                                .add(proxyPortField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                                        .addContainerGap())
+                        );
+                    }
 
-        cancelButton.setText("Cancel"); // NOI18N
-        cancelButton.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                cancelButtonActionPerformed(evt);
+                    //---- label3 ----
+                    label3.setText("<html>Note:  do not use these settings unless you receive error or warning messages about server connections.  On most systems the correct settings will be automatically copied from your web browser.");
+
+                    //---- clearAllProxyButton ----
+                    clearAllProxyButton.setText("Clear All");
+                    clearAllProxyButton.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            clearAllProxyButtonActionPerformed(e);
+                        }
+                    });
+
+                    GroupLayout jPanel15Layout = new GroupLayout(jPanel15);
+                    jPanel15.setLayout(jPanel15Layout);
+                    jPanel15Layout.setHorizontalGroup(
+                            jPanel15Layout.createParallelGroup()
+                                    .add(jPanel15Layout.createSequentialGroup()
+                                            .addContainerGap()
+                                            .add(jPanel15Layout.createParallelGroup()
+                                            .add(jPanel17, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                                            .add(jPanel15Layout.createSequentialGroup()
+                                                    .add(jPanel16, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                                    .add(33, 33, 33))
+                                            .add(jPanel15Layout.createSequentialGroup()
+                                            .add(label3, GroupLayout.PREFERRED_SIZE, 674, GroupLayout.PREFERRED_SIZE)
+                                            .addContainerGap())))
+                                    .add(jPanel15Layout.createSequentialGroup()
+                                    .add(17, 17, 17)
+                                    .add(clearAllProxyButton)
+                                    .addContainerGap(667, Short.MAX_VALUE))
+                    );
+                    jPanel15Layout.setVerticalGroup(
+                            jPanel15Layout.createParallelGroup()
+                                    .add(jPanel15Layout.createSequentialGroup()
+                                    .add(label3, GroupLayout.PREFERRED_SIZE, 82, GroupLayout.PREFERRED_SIZE)
+                                    .addPreferredGap(LayoutStyle.RELATED)
+                                    .add(jPanel17, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                                    .add(18, 18, 18)
+                                    .add(jPanel16, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                    .add(32, 32, 32)
+                                    .add(clearAllProxyButton)
+                                    .add(87, 87, 87))
+                    );
+                }
+                proxyPanel.add(jPanel15);
             }
-        });
-        okCancelButtonPanel.add(cancelButton);
+            tabbedPane.addTab("Proxy", proxyPanel);
 
-        getContentPane().add(okCancelButtonPanel, java.awt.BorderLayout.SOUTH);
+        }
+        contentPane.add(tabbedPane, BorderLayout.CENTER);
+
+        //======== okCancelButtonPanel ========
+        {
 
+            //---- okButton ----
+            okButton.setText("OK");
+            okButton.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+                    okButtonActionPerformed(e);
+                }
+            });
+            okCancelButtonPanel.add(okButton);
+
+            //---- cancelButton ----
+            cancelButton.setText("Cancel");
+            cancelButton.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+                    cancelButtonActionPerformed(e);
+                }
+            });
+            okCancelButtonPanel.add(cancelButton);
+        }
+        contentPane.add(okCancelButtonPanel, BorderLayout.SOUTH);
         pack();
+        setLocationRelativeTo(getOwner());
     }// </editor-fold>//GEN-END:initComponents
 
     private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
@@ -1240,7 +1834,7 @@ public class PreferencesEditor extends javax.swing.JDialog {
             lastSelectedIndex = tabbedPane.getSelectedIndex();
 
             // Store the changed preferences
-            preferenceManager.putAll(updatedPreferenceMap);
+            prefMgr.putAll(updatedPreferenceMap);
 
             if (updatedPreferenceMap.containsKey(PreferenceManager.PORT_ENABLED) ||
                     updatedPreferenceMap.containsKey(PreferenceManager.PORT_NUMBER)) {
@@ -1250,62 +1844,73 @@ public class PreferencesEditor extends javax.swing.JDialog {
                 }
             }
 
-            // Clear the map that holds recents preference changes
-            updatedPreferenceMap.clear();
-
             checkForSAMChanges();
 
             // Overlays
             if (updateOverlays) {
-                TrackManager.getInstance().resetOverlayTracks();
+                IGVMainFrame.getInstance().getTrackManager().resetOverlayTracks();
+            }
+
+            // Proxies
+            if (proxySettingsChanged) {
+                IGVHttpUtils.updateProxySettings();
             }
 
+            updatedPreferenceMap.clear();
+
             IGVMainFrame.getInstance().repaint();
             setVisible(false);
+
         } else {
             resetValidation();
         }
-    }//GEN-LAST:event_okButtonActionPerformed
+    }
 
     private void expMapToLociCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_expMapToLociCBActionPerformed
-        updatedPreferenceMap.put(PreferenceManager.PROBE_MAPPING_KEY, String.valueOf(
-                expMapToGeneCB.isSelected()));
-    }//GEN-LAST:event_expMapToLociCBActionPerformed
+        updatedPreferenceMap.put(PreferenceManager.PROBE_MAPPING_KEY, String.valueOf(expMapToGeneCB.isSelected()));
+    }
 
     private void clearGenomeCacheButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clearGenomeCacheButtonActionPerformed
         GenomeManager.getInstance().clearGenomeCache();
         JOptionPane.showMessageDialog(this, "<html>Cached genomes have been removed.");
-    }//GEN-LAST:event_clearGenomeCacheButtonActionPerformed
+    }
 
     private void editServerPropertiesCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editServerPropertiesCBActionPerformed
         boolean edit = editServerPropertiesCB.isSelected();
         dataServerURLTextField.setEnabled(edit);
         genomeServerURLTextField.setEnabled(edit);
-    }//GEN-LAST:event_editServerPropertiesCBActionPerformed
+    }
 
     private void dataServerURLTextFieldFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_dataServerURLTextFieldFocusLost
         String attributeName = dataServerURLTextField.getText().trim();
         updatedPreferenceMap.put(PreferenceManager.DATA_SERVER_URL_KEY, attributeName);
-    }//GEN-LAST:event_dataServerURLTextFieldFocusLost
+    }
 
     private void dataServerURLTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dataServerURLTextFieldActionPerformed
         String attributeName = dataServerURLTextField.getText().trim();
         updatedPreferenceMap.put(PreferenceManager.DATA_SERVER_URL_KEY, attributeName);
-    }//GEN-LAST:event_dataServerURLTextFieldActionPerformed
+    }
 
     private void genomeServerURLTextFieldFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_genomeServerURLTextFieldFocusLost
         String attributeName = genomeServerURLTextField.getText().trim();
         updatedPreferenceMap.put(PreferenceManager.GENOMES_SEQUENCE_URL, attributeName);
-    }//GEN-LAST:event_genomeServerURLTextFieldFocusLost
+    }
 
     private void genomeServerURLTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_genomeServerURLTextFieldActionPerformed
         String attributeName = genomeServerURLTextField.getText().trim();
         updatedPreferenceMap.put(PreferenceManager.GENOMES_SEQUENCE_URL, attributeName);
-    }//GEN-LAST:event_genomeServerURLTextFieldActionPerformed
+    }
+
+
+    private void expandIconCBActionPerformed(ActionEvent e) {
+        updatedPreferenceMap.put(PreferenceManager.SHOW_EXPAND_ICON, String.valueOf(expandIconCB.isSelected()));
+
+    }
+
 
     private void insertSizeThresholdFieldFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_insertSizeThresholdFieldFocusLost
         this.insertSizeThresholdFieldActionPerformed(null);
-    }//GEN-LAST:event_insertSizeThresholdFieldFocusLost
+    }
 
     private void insertSizeThresholdFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_insertSizeThresholdFieldActionPerformed
         String insertThreshold = insertSizeThresholdField.getText().trim();
@@ -1315,9 +1920,9 @@ public class PreferencesEditor extends javax.swing.JDialog {
                     insertThreshold);
         } catch (NumberFormatException numberFormatException) {
             inputValidated = false;
-            MessageUtils.showMessage("Mapping quality threshold must be an integer.");
+            MessageUtils.showMessage("BlastMapping quality threshold must be an integer.");
         }
-    }//GEN-LAST:event_insertSizeThresholdFieldActionPerformed
+    }
 
     private void mappingQualityThresholdFieldFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_mappingQualityThresholdFieldFocusLost
         mappingQualityThresholdFieldActionPerformed(null);
@@ -1332,7 +1937,7 @@ public class PreferencesEditor extends javax.swing.JDialog {
         } catch (NumberFormatException numberFormatException) {
             inputValidated = false;
             MessageUtils.showMessage(
-                    "Mapping quality threshold must be an integer.");
+                    "BlastMapping quality threshold must be an integer.");
         }
     }//GEN-LAST:event_mappingQualityThresholdFieldActionPerformed
 
@@ -1341,11 +1946,10 @@ public class PreferencesEditor extends javax.swing.JDialog {
     }//GEN-LAST:event_samMaxLevelFieldFocusLost
 
     private void samMaxLevelFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_samMaxLevelFieldActionPerformed
-        String maxLevelString = samMaxLevelField.getText().trim();
+        String maxLevelString = samMaxLevelsField.getText().trim();
         try {
             Integer.parseInt(maxLevelString);
-            updatedPreferenceMap.put(PreferenceManager.SAMPreferences.MAX_LEVELS,
-                    maxLevelString);
+            updatedPreferenceMap.put(PreferenceManager.SAMPreferences.MAX_LEVELS, maxLevelString);
         } catch (NumberFormatException numberFormatException) {
             inputValidated = false;
             MessageUtils.showMessage("Maximum read depth must be an integer.");
@@ -1365,43 +1969,52 @@ public class PreferencesEditor extends javax.swing.JDialog {
         updatedPreferenceMap.put(
                 PreferenceManager.SAMPreferences.FLAG_UNMAPPED_PAIR,
                 String.valueOf(samFlagUnmappedPairCB.isSelected()));
-    }//GEN-LAST:event_samFlagUnmappedPairCBActionPerformed
+    }
 
     private void samShowDuplicatesCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_samShowDuplicatesCBActionPerformed
         updatedPreferenceMap.put(
                 PreferenceManager.SAMPreferences.SHOW_DUPLICATES,
-                String.valueOf(samShowDuplicatesCB.isSelected()));
-    }//GEN-LAST:event_samShowDuplicatesCBActionPerformed
+                String.valueOf(!samFilterDuplicatesCB.isSelected()));
+    }
+
+    private void showSoftClippedCBActionPerformed(ActionEvent e) {
+        updatedPreferenceMap.put(
+                PreferenceManager.SAMPreferences.SHOW_SOFT_CLIPPED,
+                String.valueOf(showSoftClippedCB.isSelected()));
+    }
+
 
     private void samMaxWindowSizeFieldFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_samMaxWindowSizeFieldFocusLost
         String maxSAMWindowSize = samMaxWindowSizeField.getText().trim();
         try {
             Float.parseFloat(maxSAMWindowSize);
-            updatedPreferenceMap.put(PreferenceManager.SAMPreferences.MAX_VISIBLE_RANGE,
-                    maxSAMWindowSize);
+            updatedPreferenceMap.put(PreferenceManager.SAMPreferences.MAX_VISIBLE_RANGE, maxSAMWindowSize);
         } catch (NumberFormatException numberFormatException) {
             inputValidated = false;
             MessageUtils.showMessage("Visibility range must be a number.");
         }
     }//GEN-LAST:event_samMaxWindowSizeFieldFocusLost
 
-    private void samMaxWindowSizeFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_samMaxWindowSizeFieldActionPerformed
+    private void samMaxWindowSizeFieldActionPerformed(java.awt.event.ActionEvent evt) {
         String maxSAMWindowSize = String.valueOf(samMaxWindowSizeField.getText());
         try {
             Float.parseFloat(maxSAMWindowSize);
-            updatedPreferenceMap.put(PreferenceManager.SAMPreferences.MAX_VISIBLE_RANGE,
-                    maxSAMWindowSize);
+            updatedPreferenceMap.put(PreferenceManager.SAMPreferences.MAX_VISIBLE_RANGE, maxSAMWindowSize);
         } catch (NumberFormatException numberFormatException) {
             inputValidated = false;
             MessageUtils.showMessage("Visibility range must be a number.");
         }
-    }//GEN-LAST:event_samMaxWindowSizeFieldActionPerformed
+    }
 
-    private void chartDrawTrackNameCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chartDrawTrackNameCBActionPerformed
-        updatedPreferenceMap.put(
-                PreferenceManager.ChartPreferences.DRAW_TRACK_NAME,
+    private void chartDrawTrackNameCBActionPerformed(java.awt.event.ActionEvent evt) {
+        updatedPreferenceMap.put(PreferenceManager.ChartPreferences.DRAW_TRACK_NAME,
                 String.valueOf(chartDrawTrackNameCB.isSelected()));
-    }//GEN-LAST:event_chartDrawTrackNameCBActionPerformed
+    }
+
+    private void autoscaleCBActionPerformed(java.awt.event.ActionEvent evt) {
+        updatedPreferenceMap.put(PreferenceManager.ChartPreferences.AUTOSCALE, String.valueOf(autoscaleCB.isSelected()));
+    }
+
 
     private void colorBordersCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_colorBordersCBActionPerformed
         updatedPreferenceMap.put(
@@ -1453,10 +2066,9 @@ public class PreferencesEditor extends javax.swing.JDialog {
     private void overlayAttributeTextFieldFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_overlayAttributeTextFieldFocusLost
         String attributeName = String.valueOf(overlayAttributeTextField.getText());
         if (attributeName != null) {
-            attributeName = attributeName.toUpperCase().trim();
+            attributeName = attributeName.trim();
         }
-        updatedPreferenceMap.put(PreferenceManager.OVERLAY_ATTRIBUTE_KEY,
-                attributeName);
+        updatedPreferenceMap.put(PreferenceManager.OVERLAY_ATTRIBUTE_KEY, attributeName);
         updateOverlays = true;
     }//GEN-LAST:event_overlayAttributeTextFieldFocusLost
 
@@ -1553,7 +2165,14 @@ public class PreferencesEditor extends javax.swing.JDialog {
                 PreferenceManager.SAMPreferences.SHOW_REF_SEQ,
                 String.valueOf(showRefSeqCB.isSelected()));
 
-    }//GEN-LAST:event_showRefSeqCBActionPerformed
+    }
+
+    private void filterVendorFailedReadsCBActionPerformed(ActionEvent e) {
+        updatedPreferenceMap.put(
+                PreferenceManager.SAMPreferences.FILTER_FAILED_READS,
+                String.valueOf(filterFailedReadsCB.isSelected()));
+    }
+
 
     private void samMinBaseQualityFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_samMinBaseQualityFieldActionPerformed
         String baseQuality = samMinBaseQualityField.getText().trim();
@@ -1587,35 +2206,26 @@ public class PreferencesEditor extends javax.swing.JDialog {
     }//GEN-LAST:event_samMaxBaseQualityFieldFocusLost
 
     private void expMapToGeneCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_expMapToGeneCBActionPerformed
-        updatedPreferenceMap.put(PreferenceManager.PROBE_MAPPING_KEY, String.valueOf(
-                expMapToGeneCB.isSelected()));
+        updatedPreferenceMap.put(PreferenceManager.PROBE_MAPPING_KEY, String.valueOf(expMapToGeneCB.isSelected()));
 
     }//GEN-LAST:event_expMapToGeneCBActionPerformed
 
     private void labelYAxisCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_labelYAxisCBActionPerformed
-        updatedPreferenceMap.put(PreferenceManager.ChartPreferences.Y_AXIS, String.valueOf(
-                labelYAxisCB.isSelected()));
+        updatedPreferenceMap.put(PreferenceManager.ChartPreferences.Y_AXIS, String.valueOf(labelYAxisCB.isSelected()));
     }//GEN-LAST:event_labelYAxisCBActionPerformed
 
     private void shadeCenterCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_shadeCenterCBActionPerformed
-        updatedPreferenceMap.put(PreferenceManager.SAMPreferences.SHADE_CENTER, String.valueOf(
-                shadeCenterCB.isSelected()));
+        updatedPreferenceMap.put(PreferenceManager.SAMPreferences.SHADE_CENTER, String.valueOf(shadeCenterCB.isSelected()));
 
     }//GEN-LAST:event_shadeCenterCBActionPerformed
 
     private void showCovTrackCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showCovTrackCBActionPerformed
-        updatedPreferenceMap.put(
-                PreferenceManager.SAMPreferences.SHOW_COV_TRACK,
-                String.valueOf(showCovTrackCB.isSelected()));
+        updatedPreferenceMap.put(PreferenceManager.SAMPreferences.SHOW_COV_TRACK, String.valueOf(showCovTrackCB.isSelected()));
     }//GEN-LAST:event_showCovTrackCBActionPerformed
 
     private void filterCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_filterCBActionPerformed
-
-        updatedPreferenceMap.put(
-                PreferenceManager.SAMPreferences.FILTER_ALIGNMENTS,
-                String.valueOf(filterCB.isSelected()));
+        updatedPreferenceMap.put(PreferenceManager.SAMPreferences.FILTER_ALIGNMENTS, String.valueOf(filterCB.isSelected()));
         filterURL.setEnabled(filterCB.isSelected());
-
     }//GEN-LAST:event_filterCBActionPerformed
 
     private void filterURLActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_filterURLActionPerformed
@@ -1645,10 +2255,7 @@ public class PreferencesEditor extends javax.swing.JDialog {
     }//GEN-LAST:event_portFieldFocusLost
 
     private void enablePortCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_enablePortCBActionPerformed
-
-        updatedPreferenceMap.put(
-                PreferenceManager.PORT_ENABLED,
-                String.valueOf(enablePortCB.isSelected()));
+        updatedPreferenceMap.put(PreferenceManager.PORT_ENABLED, String.valueOf(enablePortCB.isSelected()));
         portField.setEnabled(enablePortCB.isSelected());
 
     }//GEN-LAST:event_enablePortCBActionPerformed
@@ -1663,25 +2270,139 @@ public class PreferencesEditor extends javax.swing.JDialog {
         // TODO add your handling code here:
         PreferenceManager prefMgr = PreferenceManager.getInstance();
         genomeServerURLTextField.setEnabled(true);
-        genomeServerURLTextField.setText(IGVConstants.DEFAULT_SERVER_GENOME_ARCHIVE_LIST);
+        genomeServerURLTextField.setText(PreferenceManager.DEFAULT_GENOME_SERVER_URL);
         updatedPreferenceMap.put(PreferenceManager.GENOMES_SEQUENCE_URL, null);
         dataServerURLTextField.setEnabled(true);
-        dataServerURLTextField.setText(IGVConstants.DATA_SERVER_REGISTRY);
+        dataServerURLTextField.setText(PreferenceManager.DEFAULT_DATA_SERVER_URL);
         updatedPreferenceMap.put(PreferenceManager.DATA_SERVER_URL_KEY, null);
     }//GEN-LAST:event_jButton1ActionPerformed
 
     private void searchZoomCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchZoomCBActionPerformed
-        updatedPreferenceMap.put(
-                PreferenceManager.SEARCH_ZOOM,
-                String.valueOf(searchZoomCB.isSelected()));
+        updatedPreferenceMap.put(PreferenceManager.SEARCH_ZOOM, String.valueOf(searchZoomCB.isSelected()));
     }//GEN-LAST:event_searchZoomCBActionPerformed
 
     private void useByteRangeCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useByteRangeCBActionPerformed
-        updatedPreferenceMap.put(
-                PreferenceManager.USE_BYTE_RANGE,
-                String.valueOf(useByteRangeCB.isSelected()));
+        updatedPreferenceMap.put(PreferenceManager.USE_BYTE_RANGE, String.valueOf(useByteRangeCB.isSelected()));
     }//GEN-LAST:event_useByteRangeCBActionPerformed
 
+    private void showDatarangeCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showDatarangeCBActionPerformed
+        updatedPreferenceMap.put(PreferenceManager.ChartPreferences.SHOW_DATA_RANGE, String.valueOf(showDatarangeCB.isSelected()));
+    }//GEN-LAST:event_showDatarangeCBActionPerformed
+
+    private void showDatarangeCBFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_showDatarangeCBFocusLost
+        showDatarangeCBActionPerformed(null);
+    }//GEN-LAST:event_showDatarangeCBFocusLost
+
+    private void snpThresholdFieldActionPerformed(java.awt.event.ActionEvent evt) {
+        String snpThreshold = snpThresholdField.getText().trim();
+        try {
+            Double.parseDouble(snpThreshold);
+            updatedPreferenceMap.put(PreferenceManager.SAMPreferences.ALLELE_THRESHOLD, snpThreshold);
+        } catch (NumberFormatException numberFormatException) {
+            inputValidated = false;
+            MessageUtils.showMessage("Allele frequency threshold must be a number.");
+        }
+    }
+
+    private void snpThresholdFieldFocusLost(java.awt.event.FocusEvent evt) {
+        snpThresholdFieldActionPerformed(null);
+    }
+
+    private void normalizeCoverageCBActionPerformed(java.awt.event.ActionEvent evt) {
+        updatedPreferenceMap.put(PreferenceManager.NORMALIZE_COVERAGE, String.valueOf(normalizeCoverageCB.isSelected()));
+        portField.setEnabled(enablePortCB.isSelected());
+
+    }
+
+
+    // Proxy settings
+
+    private void useProxyCBActionPerformed(java.awt.event.ActionEvent evt) {
+        proxySettingsChanged = true;
+        boolean useProxy = useProxyCB.isSelected();
+        boolean authenticateProxy = authenticateProxyCB.isSelected();
+        portField.setEnabled(enablePortCB.isSelected());
+        updateProxyState(useProxy, authenticateProxy);
+        updatedPreferenceMap.put(PreferenceManager.USE_PROXY, String.valueOf(useProxy));
+
+    }
+
+
+    private void authenticateProxyCBActionPerformed(java.awt.event.ActionEvent evt) {
+        proxySettingsChanged = true;
+        boolean useProxy = useProxyCB.isSelected();
+        boolean authenticateProxy = authenticateProxyCB.isSelected();
+        portField.setEnabled(enablePortCB.isSelected());
+        updateProxyState(useProxy, authenticateProxy);
+        updatedPreferenceMap.put(PreferenceManager.PROXY_AUTHENTICATE, String.valueOf(authenticateProxy));
+
+    }
+
+
+    // Host
+
+    private void proxyHostFieldFocusLost(java.awt.event.FocusEvent evt) {
+        proxyHostFieldActionPerformed(null);
+    }
+
+    private void proxyHostFieldActionPerformed(java.awt.event.ActionEvent evt) {
+        proxySettingsChanged = true;
+        updatedPreferenceMap.put(PreferenceManager.PROXY_HOST, proxyHostField.getText());
+    }
+
+    private void proxyPortFieldFocusLost(java.awt.event.FocusEvent evt) {
+        proxyPortFieldActionPerformed(null);
+    }
+
+    private void proxyPortFieldActionPerformed(java.awt.event.ActionEvent evt) {
+        try {
+            if (proxyPortField.getText().trim().length() > 0) {
+                Integer.parseInt(proxyPortField.getText());
+            }
+            proxySettingsChanged = true;
+            updatedPreferenceMap.put(PreferenceManager.PROXY_PORT, proxyPortField.getText());
+
+        }
+        catch (NumberFormatException e) {
+            MessageUtils.showMessage("Proxy port must be an integer.");
+        }
+    }
+
+    // Username
+
+    private void proxyUsernameFieldFocusLost(java.awt.event.FocusEvent evt) {
+        proxyUsernameFieldActionPerformed(null);
+    }
+
+    private void proxyUsernameFieldActionPerformed(java.awt.event.ActionEvent evt) {
+        proxySettingsChanged = true;
+        String user = proxyUsernameField.getText();
+        updatedPreferenceMap.put(PreferenceManager.PROXY_USER, user);
+
+    }
+
+    // Password
+
+    private void proxyPasswordFieldFocusLost(java.awt.event.FocusEvent evt) {
+        proxyPasswordFieldActionPerformed(null);
+    }
+
+    private void proxyPasswordFieldActionPerformed(java.awt.event.ActionEvent evt) {
+        proxySettingsChanged = true;
+        String pw = proxyPasswordField.getText();
+        String pwEncoded = Utilities.base64Encode(pw);
+        updatedPreferenceMap.put(PreferenceManager.PROXY_PW, pwEncoded);
+
+    }
+
+
+    private void updateProxyState(boolean useProxy, boolean authenticateProxy) {
+        proxyHostField.setEnabled(useProxy);
+        proxyPortField.setEnabled(useProxy);
+        proxyUsernameField.setEnabled(useProxy && authenticateProxy);
+        proxyPasswordField.setEnabled(useProxy && authenticateProxy);
+    }
+
     private void resetValidation() {
         // Assume valid input until proven otherwise
         inputValidated = true;
@@ -1694,34 +2415,35 @@ public class PreferencesEditor extends javax.swing.JDialog {
     PreferenceManager.USER_PROBE_MAP_KEY,
     filename);
      * */
+
     private void initValues() {
-        combinePanelsCB.setSelected(preferenceManager.getShowSingleTrackPane());
+        combinePanelsCB.setSelected(prefMgr.getShowSingleTrackPane());
         //drawExonNumbersCB.setSelected(preferenceManager.getDrawExonNumbers());
-        defaultChartTrackHeightField.setText(String.valueOf(preferenceManager.getDefaultChartTrackHeight()));
-        defaultTrackHeightField.setText(String.valueOf(preferenceManager.getDefaultTrackHeight()));
-        displayTracksCB.setSelected(preferenceManager.getDiplayOverlayTracks());
-        overlayAttributeTextField.setText(preferenceManager.getOverlayAttribute());
-        overlayTrackCB.setSelected(preferenceManager.getOverlayTracks());
-        showMissingDataCB.setSelected(preferenceManager.getShowMissingData());
-        joinSegmentsCB.setSelected(preferenceManager.isJoinAdjacentSegments());
-        colorOverlyCB.setSelected(preferenceManager.getColorOverlay());
+        defaultChartTrackHeightField.setText(String.valueOf(prefMgr.getDefaultChartTrackHeight()));
+        defaultTrackHeightField.setText(String.valueOf(prefMgr.getDefaultTrackHeight()));
+        displayTracksCB.setSelected(prefMgr.getDiplayOverlayTracks());
+        overlayAttributeTextField.setText(prefMgr.getOverlayAttribute());
+        overlayTrackCB.setSelected(prefMgr.getOverlayTracks());
+        showMissingDataCB.setSelected(prefMgr.getShowMissingData());
+        joinSegmentsCB.setSelected(prefMgr.isJoinAdjacentSegments());
+        colorOverlyCB.setSelected(prefMgr.getColorOverlay());
         overlayAttributeTextField.setEnabled(overlayTrackCB.isSelected());
         colorOverlyCB.setEnabled(overlayTrackCB.isSelected());
         chooseOverlayColorsButton.setEnabled(overlayTrackCB.isSelected());
 
-        enablePortCB.setSelected(preferenceManager.isPortEnabled());
-        portField.setText(String.valueOf(preferenceManager.getPortNumber()));
+        enablePortCB.setSelected(prefMgr.isPortEnabled());
+        portField.setText(String.valueOf(prefMgr.getPortNumber()));
         portField.setEnabled(enablePortCB.isSelected());
 
-        expandCB.setSelected(preferenceManager.isExpandTracks());
-        searchZoomCB.setSelected(preferenceManager.getBooleanPreference(PreferenceManager.SEARCH_ZOOM, true));
+        expandCB.setSelected(prefMgr.isExpandTracks());
+        searchZoomCB.setSelected(prefMgr.getBooleanPreference(PreferenceManager.SEARCH_ZOOM, true));
 
-        useByteRangeCB.setSelected(preferenceManager.getBooleanPreference(PreferenceManager.USE_BYTE_RANGE, true));
-        showAttributesDisplayCheckBox.setSelected(preferenceManager.getShowAttributeView());
-        trackNameAttributeField.setText(preferenceManager.getTrackAttributeName());
+        useByteRangeCB.setSelected(prefMgr.getBooleanPreference(PreferenceManager.USE_BYTE_RANGE, true));
+        showAttributesDisplayCheckBox.setSelected(prefMgr.getShowAttributeView());
+        trackNameAttributeField.setText(prefMgr.getTrackAttributeName());
         PreferenceManager prefManager = PreferenceManager.getInstance();
         genomeServerURLTextField.setText(prefManager.getGenomeListURL());
-        dataServerURLTextField.setText(preferenceManager.getDataServerURL());
+        dataServerURLTextField.setText(prefMgr.getDataServerURL());
 
         // Chart panel
         PreferenceManager.ChartPreferences cp = prefManager.getChartPreferences();
@@ -1729,15 +2451,20 @@ public class PreferencesEditor extends javax.swing.JDialog {
         bottomBorderCB.setSelected(cp.isDrawBottomBorder());
         colorBordersCB.setSelected(cp.isColorBorders());
         chartDrawTrackNameCB.setSelected(cp.isDrawTrackName());
+        autoscaleCB.setSelected(cp.isAutoscale());
+        showDatarangeCB.setSelected(cp.isShowDataRange());
         labelYAxisCB.setSelected(cp.isDrawAxis());
 
         PreferenceManager.SAMPreferences samPrefs = prefManager.getSAMPreferences();
         samMaxWindowSizeField.setText(String.valueOf(samPrefs.getMaxVisibleRange()));
-        samMaxLevelField.setText(String.valueOf(samPrefs.getMaxLevels()));
+        samMaxLevelsField.setText(String.valueOf(samPrefs.getMaxLevels()));
         mappingQualityThresholdField.setText((String.valueOf(samPrefs.getQualityThreshold())));
         insertSizeThresholdField.setText((String.valueOf(samPrefs.getInsertSizeThreshold())));
+        snpThresholdField.setText((String.valueOf(samPrefs.getAlleleFreqThreshold())));
         //samShowZeroQualityCB.setSelected(samPrefs.isShowZeroQuality());
-        samShowDuplicatesCB.setSelected(samPrefs.isShowDuplicates());
+        samFilterDuplicatesCB.setSelected(!samPrefs.isShowDuplicates());
+        filterFailedReadsCB.setSelected(samPrefs.isFilterFailedReads());
+        showSoftClippedCB.setSelected(samPrefs.isShowSoftClipped());
         samFlagUnmappedPairCB.setSelected(samPrefs.isFlagUnmappedPair());
         showRefSeqCB.setSelected(samPrefs.isShowRefSequence());
         samShadeMismatchedBaseCB.setSelected(samPrefs.isShadeBaseQuality());
@@ -1747,39 +2474,51 @@ public class PreferencesEditor extends javax.swing.JDialog {
         samMaxBaseQualityField.setEnabled(samShadeMismatchedBaseCB.isSelected());
         shadeCenterCB.setSelected(samPrefs.isShadeCenter());
         showCovTrackCB.setSelected(samPrefs.isShowCoverageTrack());
-        initialSamMaxLevel = samPrefs.getMaxLevels();
-        initialSamQualityThreshold = samPrefs.getQualityThreshold();
-        initialShowDuplicates = samPrefs.isShowDuplicates();
         filterCB.setSelected(samPrefs.isFilterAlignments());
         if (samPrefs.getFilterURL() != null) {
             filterURL.setText(samPrefs.getFilterURL());
         }
 
 
-        expMapToGeneCB.setSelected(preferenceManager.isMapProbesToGenes());
-        expMapToLociCB.setSelected(!preferenceManager.isMapProbesToGenes());
+        expMapToGeneCB.setSelected(prefMgr.isMapProbesToGenes());
+        expMapToLociCB.setSelected(!prefMgr.isMapProbesToGenes());
+
+        normalizeCoverageCB.setSelected(prefMgr.getBooleanPreference(PreferenceManager.NORMALIZE_COVERAGE, false));
+
+        expandIconCB.setSelected(prefMgr.getBooleanPreference(PreferenceManager.SHOW_EXPAND_ICON, true));
+
+        boolean useProxy = prefMgr.getBooleanPreference(PreferenceManager.USE_PROXY, false);
+        useProxyCB.setSelected(useProxy);
+
+        boolean authenticateProxy = prefMgr.getBooleanPreference(PreferenceManager.PROXY_AUTHENTICATE, false);
+        authenticateProxyCB.setSelected(authenticateProxy);
 
-        //chartColorTrackNameCB.setSelected(cp.isColorTrackName());
-        //chartDrawYAxisCB.setSelected(cp.isDrawAxis());
+        proxyHostField.setText(prefMgr.get(PreferenceManager.PROXY_HOST, ""));
+        proxyPortField.setText(prefMgr.get(PreferenceManager.PROXY_PORT, ""));
+        proxyUsernameField.setText(prefMgr.get(PreferenceManager.PROXY_USER, ""));
+        String pwCoded = prefMgr.get(PreferenceManager.PROXY_PW, "");
+        proxyPasswordField.setText(Utilities.base64Decode(pwCoded));
+
+        updateProxyState(useProxy, authenticateProxy);
     }
 
     private void checkForSAMChanges() {
+        WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
         try {
-            int maxLevel = Integer.parseInt(samMaxLevelField.getText());
-            int qualThreshold = Integer.parseInt(mappingQualityThresholdField.getText());
-            boolean showDoops = samShowDuplicatesCB.isSelected();
-            if (maxLevel > initialSamMaxLevel || qualThreshold != initialSamQualityThreshold ||
-                    showDoops != initialShowDuplicates) {
-                LongRunningTask.submit(new Runnable() {
-
-                    public void run() {
-                        TrackManager.getInstance().reloadSAMTracks();
-                        IGVMainFrame.getInstance().repaint();
-                    }
-                });
-
+            boolean reloadSAM = false;
+            for (String key : SAM_PREFERENCE_KEYS) {
+                if (updatedPreferenceMap.containsKey(key)) {
+                    reloadSAM = true;
+                    break;
+                }
+            }
+            if (reloadSAM) {
+                IGVMainFrame.getInstance().getTrackManager().reloadSAMTracks();
+                IGVMainFrame.getInstance().repaint();
             }
         } catch (NumberFormatException numberFormatException) {
+        } finally {
+            WaitCursorManager.removeWaitCursor(token);
         }
     }
 
@@ -1820,110 +2559,153 @@ public class PreferencesEditor extends javax.swing.JDialog {
     }
 
     // Variables declaration - do not modify//GEN-BEGIN:variables
-    private javax.swing.JPanel advancedPanel;
-    private javax.swing.JPanel alignmentPanel;
-    private javax.swing.JCheckBox bottomBorderCB;
-    private javax.swing.JButton cancelButton;
-    private javax.swing.JCheckBox chartDrawTrackNameCB;
-    private javax.swing.JPanel chartPanel;
-    private com.jidesoft.swing.JideButton chooseOverlayColorsButton;
-    private javax.swing.JButton clearGenomeCacheButton;
-    private javax.swing.JCheckBox colorBordersCB;
-    private javax.swing.JCheckBox colorOverlyCB;
-    private javax.swing.JCheckBox combinePanelsCB;
-    private javax.swing.JTextField dataServerURLTextField;
-    private javax.swing.JTextField defaultChartTrackHeightField;
-    private javax.swing.JTextField defaultTrackHeightField;
-    private javax.swing.JCheckBox displayTracksCB;
-    private javax.swing.JCheckBox editServerPropertiesCB;
-    private javax.swing.JCheckBox enablePortCB;
-    private javax.swing.JRadioButton expMapToGeneCB;
-    private javax.swing.JRadioButton expMapToLociCB;
-    private javax.swing.JCheckBox expandCB;
-    private javax.swing.JPanel expressionPane;
-    private javax.swing.ButtonGroup featureStyleButtonGroup;
-    private javax.swing.JCheckBox filterCB;
-    private javax.swing.JTextField filterURL;
-    private javax.swing.JPanel generalPanel;
-    private javax.swing.JTextField genomeServerURLTextField;
-    private javax.swing.JTextField insertSizeThresholdField;
-    private javax.swing.JButton jButton1;
-    private javax.swing.JLabel jLabel1;
-    private javax.swing.JLabel jLabel10;
-    private javax.swing.JLabel jLabel11;
-    private javax.swing.JLabel jLabel12;
-    private javax.swing.JLabel jLabel13;
-    private javax.swing.JLabel jLabel14;
-    private javax.swing.JLabel jLabel15;
-    private javax.swing.JLabel jLabel16;
-    private javax.swing.JLabel jLabel17;
-    private javax.swing.JLabel jLabel18;
-    private javax.swing.JLabel jLabel19;
-    private javax.swing.JLabel jLabel2;
-    private javax.swing.JLabel jLabel20;
-    private javax.swing.JLabel jLabel21;
-    private javax.swing.JLabel jLabel22;
-    private javax.swing.JLabel jLabel23;
-    private javax.swing.JLabel jLabel24;
-    private javax.swing.JLabel jLabel25;
-    private javax.swing.JLabel jLabel3;
-    private javax.swing.JLabel jLabel4;
-    private javax.swing.JLabel jLabel5;
-    private javax.swing.JLabel jLabel6;
-    private javax.swing.JLabel jLabel7;
-    private javax.swing.JLabel jLabel8;
-    private javax.swing.JPanel jPanel1;
-    private javax.swing.JPanel jPanel10;
-    private javax.swing.JPanel jPanel2;
-    private javax.swing.JPanel jPanel3;
-    private javax.swing.JPanel jPanel4;
-    private javax.swing.JPanel jPanel5;
-    private javax.swing.JPanel jPanel6;
-    private javax.swing.JPanel jPanel7;
-    private javax.swing.JPanel jPanel8;
-    private javax.swing.JPanel jPanel9;
-    private javax.swing.JCheckBox joinSegmentsCB;
-    private java.awt.Label label1;
-    private javax.swing.JCheckBox labelYAxisCB;
-    private javax.swing.JPanel legendPanel;
-    private javax.swing.JTextField mappingQualityThresholdField;
-    private javax.swing.JLabel missingDataExplanation;
-    private javax.swing.JLabel missingDataExplanation2;
-    private javax.swing.JLabel missingDataExplanation3;
-    private javax.swing.JLabel missingDataExplanation4;
-    private javax.swing.JLabel missingDataExplanation5;
-    private javax.swing.JLabel missingDataExplanation6;
-    private javax.swing.JLabel missingDataExplanation7;
-    private javax.swing.JButton okButton;
-    private com.jidesoft.dialog.ButtonPanel okCancelButtonPanel;
-    private javax.swing.JTextField overlayAttributeTextField;
-    private javax.swing.JCheckBox overlayTrackCB;
-    private javax.swing.JPanel overlaysPanel;
-    private javax.swing.JTextField portField;
-    private javax.swing.ButtonGroup probeMappingButtonGroup;
-    private javax.swing.ButtonGroup rnaiMapapingButtonGroup;
-    private javax.swing.JCheckBox samFlagUnmappedPairCB;
-    private javax.swing.JTextField samMaxBaseQualityField;
-    private javax.swing.JTextField samMaxLevelField;
-    private javax.swing.JTextField samMaxWindowSizeField;
-    private javax.swing.JTextField samMinBaseQualityField;
-    private javax.swing.JCheckBox samShadeMismatchedBaseCB;
-    private javax.swing.JCheckBox samShowDuplicatesCB;
-    private javax.swing.JCheckBox searchZoomCB;
-    private javax.swing.JCheckBox shadeCenterCB;
-    private javax.swing.JCheckBox showAttributesDisplayCheckBox;
-    private javax.swing.JCheckBox showCovTrackCB;
-    private javax.swing.JCheckBox showMissingDataCB;
-    private javax.swing.JCheckBox showRefSeqCB;
-    private javax.swing.JTabbedPane tabbedPane;
-    private javax.swing.JCheckBox topBorderCB;
-    private javax.swing.JTextField trackNameAttributeField;
-    private javax.swing.JLabel trackNameAttributeLabel;
-    private javax.swing.JPanel tracksPanel;
-    private javax.swing.JCheckBox useByteRangeCB;
+    // Generated using JFormDesigner non-commercial license
+    private JTabbedPane tabbedPane;
+    private JPanel generalPanel;
+    private JPanel jPanel10;
+    private JLabel missingDataExplanation;
+    private JCheckBox showMissingDataCB;
+    private JCheckBox combinePanelsCB;
+    private JCheckBox joinSegmentsCB;
+    private JCheckBox showAttributesDisplayCheckBox;
+    private JCheckBox searchZoomCB;
+    private JLabel missingDataExplanation6;
+    private JLabel missingDataExplanation7;
+    private JPanel tracksPanel;
+    private JPanel jPanel6;
+    private JLabel jLabel5;
+    private JTextField defaultChartTrackHeightField;
+    private JLabel trackNameAttributeLabel;
+    private JTextField trackNameAttributeField;
+    private JLabel missingDataExplanation2;
+    private JLabel jLabel8;
+    private JTextField defaultTrackHeightField;
+    private JLabel missingDataExplanation4;
+    private JLabel missingDataExplanation5;
+    private JLabel missingDataExplanation3;
+    private JCheckBox expandCB;
+    private JCheckBox normalizeCoverageCB;
+    private JLabel missingDataExplanation8;
+    private JCheckBox expandIconCB;
+    private JPanel overlaysPanel;
+    private JPanel jPanel5;
+    private JLabel jLabel3;
+    private JTextField overlayAttributeTextField;
+    private JCheckBox overlayTrackCB;
+    private JLabel jLabel2;
+    private JCheckBox displayTracksCB;
+    private JLabel jLabel4;
+    private JCheckBox colorOverlyCB;
+    private JideButton chooseOverlayColorsButton;
+    private JPanel chartPanel;
+    private JPanel jPanel4;
+    private JCheckBox topBorderCB;
+    private Label label1;
+    private JCheckBox chartDrawTrackNameCB;
+    private JCheckBox bottomBorderCB;
+    private JLabel jLabel7;
+    private JCheckBox colorBordersCB;
+    private JCheckBox labelYAxisCB;
+    private JCheckBox autoscaleCB;
+    private JLabel jLabel9;
+    private JCheckBox showDatarangeCB;
+    private JPanel alignmentPanel;
+    private JPanel jPanel1;
+    private JPanel jPanel11;
+    private JTextField samMaxLevelsField;
+    private JTextField snpThresholdField;
+    private JLabel jLabel11;
+    private JLabel jLabel26;
+    private JLabel jLabel17;
+    private JLabel jLabel16;
+    private JTextField mappingQualityThresholdField;
+    private JLabel jLabel14;
+    private JTextField insertSizeThresholdField;
+    private JLabel jLabel13;
+    private JLabel jLabel15;
+    private JLabel jLabel18;
+    private JTextField samMaxWindowSizeField;
+    private JLabel jLabel12;
+    private JPanel jPanel12;
+    private JTextField samMinBaseQualityField;
+    private JCheckBox samShadeMismatchedBaseCB;
+    private JTextField samMaxBaseQualityField;
+    private JCheckBox showCovTrackCB;
+    private JCheckBox samFilterDuplicatesCB;
+    private JCheckBox showRefSeqCB;
+    private JLabel jLabel19;
+    private JCheckBox filterCB;
+    private JTextField filterURL;
+    private JCheckBox samFlagUnmappedPairCB;
+    private JCheckBox shadeCenterCB;
+    private JLabel jLabel10;
+    private JPanel legendPanel;
+    private JCheckBox filterFailedReadsCB;
+    private JLabel label2;
+    private JCheckBox showSoftClippedCB;
+    private JPanel expressionPane;
+    private JPanel jPanel8;
+    private JRadioButton expMapToGeneCB;
+    private JLabel jLabel24;
+    private JRadioButton expMapToLociCB;
+    private JLabel jLabel21;
+    private JPanel advancedPanel;
+    private JPanel jPanel3;
+    private JPanel jPanel2;
+    private JLabel jLabel1;
+    private JTextField genomeServerURLTextField;
+    private JLabel jLabel6;
+    private JTextField dataServerURLTextField;
+    private JCheckBox editServerPropertiesCB;
+    private JButton clearGenomeCacheButton;
+    private JButton jButton1;
+    private JPanel jPanel7;
+    private JCheckBox enablePortCB;
+    private JTextField portField;
+    private JLabel jLabel22;
+    private JPanel jPanel9;
+    private JCheckBox useByteRangeCB;
+    private JLabel jLabel25;
+    private JPanel proxyPanel;
+    private JPanel jPanel15;
+    private JPanel jPanel16;
+    private JTextField proxyUsernameField;
+    private JLabel jLabel28;
+    private JCheckBox authenticateProxyCB;
+    private JLabel jLabel29;
+    private JPasswordField proxyPasswordField;
+    private JPanel jPanel17;
+    private JTextField proxyHostField;
+    private JTextField proxyPortField;
+    private JLabel jLabel27;
+    private JLabel jLabel23;
+    private JCheckBox useProxyCB;
+    private JLabel label3;
+    private JButton clearAllProxyButton;
+    private ButtonPanel okCancelButtonPanel;
+    private JButton okButton;
+    private JButton cancelButton;
     // End of variables declaration//GEN-END:variables
 
     public boolean isCanceled() {
         return canceled;
     }
+
+
+    /**
+     * List of keys that affect the alignments loaded.  This list is used to trigger a reload, if required.
+     * Not all alignment preferences need trigger a reload, this is a subset.
+     */
+    static java.util.List<String> SAM_PREFERENCE_KEYS = Arrays.asList(
+            PreferenceManager.SAMPreferences.MAX_VISIBLE_RANGE,
+            PreferenceManager.SAMPreferences.SHOW_ZERO_QUALITY,
+            PreferenceManager.SAMPreferences.SHOW_DUPLICATES,
+            PreferenceManager.SAMPreferences.SHOW_SOFT_CLIPPED,
+            PreferenceManager.SAMPreferences.MAX_LEVELS,
+            PreferenceManager.SAMPreferences.MAX_READS,
+            PreferenceManager.SAMPreferences.ALLELE_THRESHOLD,
+            PreferenceManager.SAMPreferences.QUALITY_THRESHOLD,
+            PreferenceManager.SAMPreferences.FILTER_ALIGNMENTS,
+            PreferenceManager.SAMPreferences.FILTER_URL,
+            PreferenceManager.SAMPreferences.FILTER_FAILED_READS);
 }
diff --git a/src/org/broad/igv/ui/PreferencesEditor.jfd b/src/org/broad/igv/ui/PreferencesEditor.jfd
new file mode 100644
index 0000000..9fc2a1d
--- /dev/null
+++ b/src/org/broad/igv/ui/PreferencesEditor.jfd
@@ -0,0 +1,3074 @@
+<?xml version="1.0" encoding="UTF-8"?> 
+<java version="1.6.0_20" class="java.beans.XMLDecoder"> 
+ <object class="com.jformdesigner.model.FormModel"> 
+  <void property="contentType"> 
+   <string>form/swing</string> 
+  </void> 
+  <void property="root"> 
+   <object class="com.jformdesigner.model.FormRoot"> 
+    <void method="add"> 
+     <object class="com.jformdesigner.model.FormWindow"> 
+      <string>javax.swing.JDialog</string> 
+      <object class="com.jformdesigner.model.FormLayoutManager"> 
+       <class>java.awt.BorderLayout</class> 
+      </object> 
+      <void method="setProperty"> 
+       <string>defaultCloseOperation</string> 
+       <int>2</int> 
+      </void> 
+      <void method="setProperty"> 
+       <string>resizable</string> 
+       <boolean>false</boolean> 
+      </void> 
+      <void method="add"> 
+       <object class="com.jformdesigner.model.FormContainer"> 
+        <string>javax.swing.JTabbedPane</string> 
+        <object class="com.jformdesigner.model.FormLayoutManager"> 
+         <class>javax.swing.JTabbedPane</class> 
+        </object> 
+        <void property="name"> 
+         <string>tabbedPane</string> 
+        </void> 
+        <void method="add"> 
+         <object class="com.jformdesigner.model.FormContainer"> 
+          <string>javax.swing.JPanel</string> 
+          <object class="com.jformdesigner.model.FormLayoutManager"> 
+           <class>com.jformdesigner.runtime.NullLayout</class> 
+          </object> 
+          <void property="name"> 
+           <string>generalPanel</string> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormContainer"> 
+            <string>javax.swing.JPanel</string> 
+            <object class="com.jformdesigner.model.FormLayoutManager"> 
+             <class>org.jdesktop.layout.GroupLayout</class> 
+             <void method="setProperty"> 
+              <string>$horizontalGroup</string> 
+              <string>par l {seq l {space :::p, par l {seq l {space :p:35:p, comp missingDataExplanation:::p::p}, comp showMissingDataCB::l:p::p, comp combinePanelsCB::l:p::p, comp joinSegmentsCB::l:p::p, comp searchZoomCB::l:p::p, comp showAttributesDisplayCheckBox::l:p::p, seq l {space :p:44:p, par t {comp missingDataExplanation7::t:p:508:p, comp missingDataExplanation6::t:p::p}}}, space ::50:x}}</string> 
+             </void> 
+             <void method="setProperty"> 
+              <string>$verticalGroup</string> 
+              <string>par l {seq {space :::p, comp showMissingDataCB:::p::p, space :::p, comp missingDataExplanation:::p::p, space s:::p, comp combinePanelsCB:::p::p, space :p:26:p, comp joinSegmentsCB:::p::p, space :::p, comp missingDataExplanation6:::p::p, space :p:18:p, comp showAttributesDisplayCheckBox:::p::p, space :p:28:p, comp searchZoomCB:::p::p, space :::p, comp missingDataExplanation7:::p:78:p, space ::53:x}}</string> 
+             </void> 
+            </object> 
+            <void property="name"> 
+             <string>jPanel10</string> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>font</string> 
+               <object class="java.awt.Font"> 
+                <string>Lucida Grande</string> 
+                <int>2</int> 
+                <int>12</int> 
+               </object> 
+              </void> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>&lt;html&gt;Distinguish  regions with zero values from regions with no data on plots &lt;br&gt;(e.g. bar charts).  Regions with no data are indicated with a gray background.</string> 
+              </void> 
+              <void property="name"> 
+               <string>missingDataExplanation</string> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Distinguish Missing Data</string> 
+              </void> 
+              <void property="name"> 
+               <string>showMissingDataCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>showMissingDataCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Combine Data and Feature Panels</string> 
+              </void> 
+              <void property="name"> 
+               <string>combinePanelsCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>combinePanelsCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Join Adjacent CopyNumber Segments</string> 
+              </void> 
+              <void property="name"> 
+               <string>joinSegmentsCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>joinSegmentsCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Show Attribute Display</string> 
+              </void> 
+              <void property="name"> 
+               <string>showAttributesDisplayCheckBox</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>showAttributesDisplayCheckBoxActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Zoom to features</string> 
+              </void> 
+              <void property="name"> 
+               <string>searchZoomCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>searchZoomCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>font</string> 
+               <object class="java.awt.Font"> 
+                <string>Lucida Grande</string> 
+                <int>2</int> 
+                <int>12</int> 
+               </object> 
+              </void> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>&lt;html&gt;This option applies to segmented copy number data only.  When selected, gaps between&lt;br&gt;adjacent segments are filled by extending segment endpoints.</string> 
+              </void> 
+              <void property="name"> 
+               <string>missingDataExplanation6</string> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>font</string> 
+               <object class="java.awt.Font"> 
+                <string>Lucida Grande</string> 
+                <int>2</int> 
+                <int>12</int> 
+               </object> 
+              </void> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>&lt;html&gt;This option controls the behavior of feature searchs.  If true, the zoom level is changed as required to size the view to the feature size.  If false the zoom level is unchanged.</string> 
+              </void> 
+              <void method="setProperty"> 
+               <string>verticalAlignment</string> 
+               <int>1</int> 
+              </void> 
+              <void property="name"> 
+               <string>missingDataExplanation7</string> 
+              </void> 
+             </object> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.NullConstraints</class> 
+            <void method="setProperty"> 
+             <string>width</string> 
+             <int>610</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>height</string> 
+             <int>420</int> 
+            </void> 
+           </object> 
+          </void> 
+         </object> 
+         <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+          <null/> 
+          <void method="setProperty"> 
+           <string>title</string> 
+           <string>General</string> 
+          </void> 
+         </object> 
+        </void> 
+        <void method="add"> 
+         <object class="com.jformdesigner.model.FormContainer"> 
+          <string>javax.swing.JPanel</string> 
+          <object class="com.jformdesigner.model.FormLayoutManager"> 
+           <class>com.jformdesigner.runtime.NullLayout</class> 
+          </object> 
+          <void property="name"> 
+           <string>tracksPanel</string> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormContainer"> 
+            <string>javax.swing.JPanel</string> 
+            <object class="com.jformdesigner.model.FormLayoutManager"> 
+             <class>org.jdesktop.layout.GroupLayout</class> 
+             <void method="setProperty"> 
+              <string>$horizontalGroup</string> 
+              <string>par l {seq {par l {seq {space :::p, par l {seq l {space :p:24:p, comp missingDataExplanation4:::p:354:p}, seq l {comp trackNameAttributeLabel:::p::p, space :::p, comp trackNameAttributeField:::p:216:p}, seq l {comp jLabel8:::p::p, space :p:39:p, comp defaultTrackHeightField:::p:57:p}, seq l {space :p:24:p, comp missingDataExplanation5::::1141:x}, seq l {comp jLabel5:::p::p, space :p:36:p, comp defaultChartTrackHeightField:::p:57:p}}}, seq l {space :::p, comp expandIconCB:::p::p, space :p:724:p, comp missingDataExplanation3:::p::p}, seq l {space :p:63:p, comp missingDataExplanation2:::p:578:p}, seq l {space :::p, comp expandCB:::p::p}, seq l {space :::p, comp normalizeCoverageCB:::p::p}, seq l {space :p:50:p, comp missingDataExplanation8:::p:608:p}}, space :::p}}</string> 
+             </void> 
+             <void method="setProperty"> 
+              <string>$verticalGroup</string> 
+              <string>par l {seq {space :::p, par b {comp jLabel5::b:p::p, comp defaultChartTrackHeightField::b:p::p}, space :::p, comp missingDataExplanation4:::p:25:p, space :::p, par b {comp jLabel8::b:p::p, comp defaultTrackHeightField::b:p::p}, space :::p, comp missingDataExplanation5:::p:25:p, space :p:26:p, par b {comp trackNameAttributeLabel::b:p::p, comp trackNameAttributeField::b:p::p}, space :::p, comp missingDataExplanation2:::p:54:p, space :p:28:p, comp expandCB:::p::p, space :p:23:p, par l {comp missingDataExplanation3:::p::p, comp expandIconCB::l:p::p}, space ::31:x, comp normalizeCoverageCB:::p::p, space s:::p, comp missingDataExplanation8:::p:52:p, space :p:15:p}}</string> 
+             </void> 
+            </object> 
+            <void property="name"> 
+             <string>jPanel6</string> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Default Track Height, Charts (Pixels)</string> 
+              </void> 
+              <void property="name"> 
+               <string>jLabel5</string> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JTextField</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>40</string> 
+              </void> 
+              <void property="name"> 
+               <string>defaultChartTrackHeightField</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>defaultChartTrackHeightFieldActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.FocusListener</string> 
+                <string>focusLost</string> 
+                <string>defaultChartTrackHeightFieldFocusLost</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Track Name Attribute</string> 
+              </void> 
+              <void property="name"> 
+               <string>trackNameAttributeLabel</string> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JTextField</string> 
+              <void property="name"> 
+               <string>trackNameAttributeField</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>trackNameAttributeFieldActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.FocusListener</string> 
+                <string>focusLost</string> 
+                <string>trackNameAttributeFieldFocusLost</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>font</string> 
+               <object class="java.awt.Font"> 
+                <string>Lucida Grande</string> 
+                <int>2</int> 
+                <int>12</int> 
+               </object> 
+              </void> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>&lt;html&gt;Name of an attribute to be used to label  tracks.  If provided tracks will be labeled with the corresponding attribute values from the sample information file</string> 
+              </void> 
+              <void method="setProperty"> 
+               <string>verticalAlignment</string> 
+               <int>1</int> 
+              </void> 
+              <void property="name"> 
+               <string>missingDataExplanation2</string> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Default Track Height, Other (Pixels)</string> 
+              </void> 
+              <void property="name"> 
+               <string>jLabel8</string> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JTextField</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>15</string> 
+              </void> 
+              <void property="name"> 
+               <string>defaultTrackHeightField</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>defaultTrackHeightFieldActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.FocusListener</string> 
+                <string>focusLost</string> 
+                <string>defaultTrackHeightFieldFocusLost</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>font</string> 
+               <object class="java.awt.Font"> 
+                <string>Lucida Grande</string> 
+                <int>2</int> 
+                <int>12</int> 
+               </object> 
+              </void> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>&lt;html&gt;Default height of chart tracks (barcharts, scatterplots, etc)</string> 
+              </void> 
+              <void property="name"> 
+               <string>missingDataExplanation4</string> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>font</string> 
+               <object class="java.awt.Font"> 
+                <string>Lucida Grande</string> 
+                <int>2</int> 
+                <int>12</int> 
+               </object> 
+              </void> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>&lt;html&gt;Default height of all other tracks</string> 
+              </void> 
+              <void property="name"> 
+               <string>missingDataExplanation5</string> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>font</string> 
+               <object class="java.awt.Font"> 
+                <string>Lucida Grande</string> 
+                <int>2</int> 
+                <int>12</int> 
+               </object> 
+              </void> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>&lt;html&gt;&lt;i&gt; If selected feature tracks are expanded by default.</string> 
+              </void> 
+              <void property="name"> 
+               <string>missingDataExplanation3</string> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Expand Feature Tracks</string> 
+              </void> 
+              <void property="name"> 
+               <string>expandCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>expandCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Normalize Coverage Data</string> 
+              </void> 
+              <void property="name"> 
+               <string>normalizeCoverageCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>normalizeCoverageCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.FocusListener</string> 
+                <string>focusLost</string> 
+                <string>normalizeCoverageCBFocusLost</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>font</string> 
+               <object class="java.awt.Font"> 
+                <string>Lucida Grande</string> 
+                <int>2</int> 
+                <int>12</int> 
+               </object> 
+              </void> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>&lt;html&gt;&lt;i&gt; Applies to coverage tracks computed with igvtools (.tdf files).  If selected coverage values are scaled by (1,000,000 / totalCount),  where totalCount is the total number of features or alignments.</string> 
+              </void> 
+              <void method="setProperty"> 
+               <string>verticalAlignment</string> 
+               <int>1</int> 
+              </void> 
+              <void property="name"> 
+               <string>missingDataExplanation8</string> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Show Expand Icon</string> 
+              </void> 
+              <void method="setProperty"> 
+               <string>toolTipText</string> 
+               <string>If checked displays an expand/collapse icon on feature tracks.</string> 
+              </void> 
+              <void property="name"> 
+               <string>expandIconCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>expandIconCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.NullConstraints</class> 
+            <void method="setProperty"> 
+             <string>x</string> 
+             <int>40</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>y</string> 
+             <int>20</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>width</string> 
+             <int>690</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>height</string> 
+             <int>480</int> 
+            </void> 
+           </object> 
+          </void> 
+         </object> 
+         <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+          <null/> 
+          <void method="setProperty"> 
+           <string>title</string> 
+           <string>Tracks</string> 
+          </void> 
+         </object> 
+        </void> 
+        <void method="add"> 
+         <object class="com.jformdesigner.model.FormContainer"> 
+          <string>javax.swing.JPanel</string> 
+          <object class="com.jformdesigner.model.FormLayoutManager"> 
+           <class>org.jdesktop.layout.GroupLayout</class> 
+           <void method="setProperty"> 
+            <string>$horizontalGroup</string> 
+            <string>par l {seq l {space :p:28:p, comp jPanel5:::p::p, space ::235:x}}</string> 
+           </void> 
+           <void method="setProperty"> 
+            <string>$verticalGroup</string> 
+            <string>par l {seq l {space :p:55:p, comp jPanel5:::p::p, space ::225:x}}</string> 
+           </void> 
+          </object> 
+          <void property="name"> 
+           <string>overlaysPanel</string> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormContainer"> 
+            <string>javax.swing.JPanel</string> 
+            <object class="com.jformdesigner.model.FormLayoutManager"> 
+             <class>org.jdesktop.layout.GroupLayout</class> 
+             <void method="setProperty"> 
+              <string>$horizontalGroup</string> 
+              <string>par l {seq {space :::p, par l {comp displayTracksCB::l:p::p, comp jLabel4::l:p::p, comp overlayTrackCB::l:p::p, comp jLabel2::l:p::p, seq l {space :p:59:p, par l {seq l {comp colorOverlyCB:::p::p, space :::p, comp chooseOverlayColorsButton:::p::p}, seq l {comp jLabel3:::p::p, space :::p, comp overlayAttributeTextField:::p:228:p}}}}, space :::p}}</string> 
+             </void> 
+             <void method="setProperty"> 
+              <string>$verticalGroup</string> 
+              <string>par l {seq {space :::p, comp jLabel2:::p::p, space :::p, comp jLabel4:::p::p, space :p:39:p, comp overlayTrackCB:::p::p, space :::p, par b {comp jLabel3::b:p::p, comp overlayAttributeTextField::b:p::p}, space :p:6:p, par t {comp colorOverlyCB:::p::p, comp chooseOverlayColorsButton:::p::p}, space :p:36:p, comp displayTracksCB:::p::p, space ::30:x}}</string> 
+             </void> 
+            </object> 
+            <void property="name"> 
+             <string>jPanel5</string> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Overlay tracks based on attribute:</string> 
+              </void> 
+              <void property="name"> 
+               <string>jLabel3</string> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JTextField</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>LINKING_ID</string> 
+              </void> 
+              <void property="name"> 
+               <string>overlayAttributeTextField</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>overlayAttributeTextFieldActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.FocusListener</string> 
+                <string>focusLost</string> 
+                <string>overlayAttributeTextFieldFocusLost</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.KeyListener</string> 
+                <string>keyTyped</string> 
+                <string>overlayAttributeTextFieldKeyTyped</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>selected</string> 
+               <boolean>true</boolean> 
+              </void> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Overlay mutation tracks</string> 
+              </void> 
+              <void method="setProperty"> 
+               <string>actionCommand</string> 
+               <string>overlayTracksCB</string> 
+              </void> 
+              <void property="name"> 
+               <string>overlayTrackCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>overlayTrackCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>font</string> 
+               <object class="java.awt.Font"> 
+                <string>Lucida Grande</string> 
+                <int>2</int> 
+                <int>12</int> 
+               </object> 
+              </void> 
+              <void property="name"> 
+               <string>jLabel2</string> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Display mutation data as distinct tracks</string> 
+              </void> 
+              <void method="setProperty"> 
+               <string>actionCommand</string> 
+               <string>displayTracksCB</string> 
+              </void> 
+              <void property="name"> 
+               <string>displayTracksCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>displayTracksCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>font</string> 
+               <object class="java.awt.Font"> 
+                <string>Lucida Grande</string> 
+                <int>2</int> 
+                <int>12</int> 
+               </object> 
+              </void> 
+              <void property="name"> 
+               <string>jLabel4</string> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Color code overlay</string> 
+              </void> 
+              <void property="name"> 
+               <string>colorOverlyCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>colorOverlyCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>com.jidesoft.swing.JideButton</string> 
+              <void method="setProperty"> 
+               <string>foreground</string> 
+               <object class="java.awt.Color"> 
+                <int>0</int> 
+                <int>0</int> 
+                <int>247</int> 
+                <int>255</int> 
+               </object> 
+              </void> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Choose colors</string> 
+              </void> 
+              <void method="setProperty"> 
+               <string>font</string> 
+               <object class="java.awt.Font"> 
+                <string>Lucida Grande</string> 
+                <int>2</int> 
+                <int>12</int> 
+               </object> 
+              </void> 
+              <void method="setProperty"> 
+               <string>verticalAlignment</string> 
+               <int>3</int> 
+              </void> 
+              <void method="setProperty"> 
+               <string>verticalTextPosition</string> 
+               <int>3</int> 
+              </void> 
+              <void property="name"> 
+               <string>chooseOverlayColorsButton</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>chooseOverlayColorsButtonActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+           </object> 
+          </void> 
+         </object> 
+         <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+          <null/> 
+          <void method="setProperty"> 
+           <string>title</string> 
+           <string>Mutations</string> 
+          </void> 
+         </object> 
+        </void> 
+        <void method="add"> 
+         <object class="com.jformdesigner.model.FormContainer"> 
+          <string>javax.swing.JPanel</string> 
+          <object class="com.jformdesigner.model.FormLayoutManager"> 
+           <class>com.jformdesigner.runtime.NullLayout</class> 
+          </object> 
+          <void property="name"> 
+           <string>chartPanel</string> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormContainer"> 
+            <string>javax.swing.JPanel</string> 
+            <object class="com.jformdesigner.model.FormLayoutManager"> 
+             <class>org.jdesktop.layout.GroupLayout</class> 
+             <void method="setProperty"> 
+              <string>$horizontalGroup</string> 
+              <string>par l {seq {space :::p, par l {comp label1::l:p::p, seq l {space :p:20:p, par l {seq l {par l {comp autoscaleCB::l:p::p, comp showDatarangeCB::l:p::p}, space s:::p, comp jLabel7::::371:x}, seq {par l {comp topBorderCB::l:p::p, comp colorBordersCB::l:p::p, comp bottomBorderCB::l:p::p, comp labelYAxisCB::l:p::p, comp chartDrawTrackNameCB::l:p::p}, space ::403:x}}}}, space :::p}};par l {seq t {space ::221:x, comp jLabel9:::p::p, space :p:84:p}}</string> 
+             </void> 
+             <void method="setProperty"> 
+              <string>$verticalGroup</string> 
+              <string>par l {seq {space :::p, comp label1:::p::p, space :::p, comp topBorderCB:::p::p, space :::p, comp bottomBorderCB:::p::p, space :p:7:p, comp colorBordersCB:::p::p, space s:::p, comp chartDrawTrackNameCB:::p::p, space :p:23:p, comp labelYAxisCB:::p::p, space s:::p, par b {comp autoscaleCB::b:p::p, comp jLabel7::b:p:50:p}, space u:::p, comp showDatarangeCB:::p::p, space :p:36:p}};par l {seq l {space :p:131:p, comp jLabel9:::p::p, space ::181:x}}</string> 
+             </void> 
+            </object> 
+            <void property="name"> 
+             <string>jPanel4</string> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Draw Top Border</string> 
+              </void> 
+              <void property="name"> 
+               <string>topBorderCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>topBorderCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>java.awt.Label</string> 
+              <void method="setProperty"> 
+               <string>font</string> 
+               <object class="com.jformdesigner.model.SwingDerivedFont"> 
+                <null/> 
+                <int>0</int> 
+                <int>0</int> 
+                <boolean>false</boolean> 
+               </object> 
+              </void> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Default settings for barcharts and scatterplots:</string> 
+              </void> 
+              <void property="name"> 
+               <string>label1</string> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Draw Track Label</string> 
+              </void> 
+              <void property="name"> 
+               <string>chartDrawTrackNameCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>chartDrawTrackNameCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Draw Bottom Border</string> 
+              </void> 
+              <void property="name"> 
+               <string>bottomBorderCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>bottomBorderCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>&lt;html&gt;&lt;i&gt;If selected charts are dynamically rescaled to the range of the data in view.</string> 
+              </void> 
+              <void property="name"> 
+               <string>jLabel7</string> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Color Borders</string> 
+              </void> 
+              <void property="name"> 
+               <string>colorBordersCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>colorBordersCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Label Y Axis</string> 
+              </void> 
+              <void property="name"> 
+               <string>labelYAxisCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>labelYAxisCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Continuous Autoscale</string> 
+              </void> 
+              <void property="name"> 
+               <string>autoscaleCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>autoscaleCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>&lt;html&gt;&lt;i&gt;Draw a label centered over the track provided&lt;br&gt;the track height is at least 25 pixels. </string> 
+              </void> 
+              <void property="name"> 
+               <string>jLabel9</string> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Show Data Range</string> 
+              </void> 
+              <void property="name"> 
+               <string>showDatarangeCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>showDatarangeCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.FocusListener</string> 
+                <string>focusLost</string> 
+                <string>showDatarangeCBFocusLost</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.NullConstraints</class> 
+            <void method="setProperty"> 
+             <string>x</string> 
+             <int>20</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>y</string> 
+             <int>30</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>width</string> 
+             <int>590</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>height</string> 
+             <int>340</int> 
+            </void> 
+           </object> 
+          </void> 
+         </object> 
+         <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+          <null/> 
+          <void method="setProperty"> 
+           <string>title</string> 
+           <string>Charts</string> 
+          </void> 
+         </object> 
+        </void> 
+        <void method="add"> 
+         <object class="com.jformdesigner.model.FormContainer"> 
+          <string>javax.swing.JPanel</string> 
+          <object class="com.jformdesigner.model.FormLayoutManager"> 
+           <class>com.jformdesigner.runtime.NullLayout</class> 
+          </object> 
+          <void property="name"> 
+           <string>alignmentPanel</string> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormContainer"> 
+            <string>javax.swing.JPanel</string> 
+            <object class="com.jformdesigner.model.FormLayoutManager"> 
+             <class>com.jformdesigner.runtime.NullLayout</class> 
+            </object> 
+            <void property="name"> 
+             <string>jPanel1</string> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormContainer"> 
+              <string>javax.swing.JPanel</string> 
+              <object class="com.jformdesigner.model.FormLayoutManager"> 
+               <class>com.jformdesigner.runtime.NullLayout</class> 
+              </object> 
+              <void property="name"> 
+               <string>jPanel11</string> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JTextField</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>jTextField1</string> 
+                </void> 
+                <void property="name"> 
+                 <string>samMaxLevelsField</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>samMaxLevelFieldActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.FocusListener</string> 
+                  <string>focusLost</string> 
+                  <string>samMaxLevelFieldFocusLost</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>206</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>41</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JTextField</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>0</string> 
+                </void> 
+                <void property="name"> 
+                 <string>snpThresholdField</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>snpThresholdFieldActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.FocusListener</string> 
+                  <string>focusLost</string> 
+                  <string>snpThresholdFieldFocusLost</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>206</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>144</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>width</string> 
+                 <int>84</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Visibility range threshold (kb)</string> 
+                </void> 
+                <void property="name"> 
+                 <string>jLabel11</string> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>6</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>12</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Coverage allele-freq threshold</string> 
+                </void> 
+                <void property="name"> 
+                 <string>jLabel26</string> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>6</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>150</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>width</string> 
+                 <int>200</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Insert size flagging threshold:</string> 
+                </void> 
+                <void property="name"> 
+                 <string>jLabel17</string> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>6</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>116</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>width</string> 
+                 <int>200</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>&lt;html&gt;&lt;i&gt;Reads with qualities  below the threshold are not shown.</string> 
+                </void> 
+                <void property="name"> 
+                 <string>jLabel16</string> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>296</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>82</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JTextField</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>0</string> 
+                </void> 
+                <void property="name"> 
+                 <string>mappingQualityThresholdField</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>mappingQualityThresholdFieldActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.FocusListener</string> 
+                  <string>focusLost</string> 
+                  <string>mappingQualityThresholdFieldFocusLost</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>206</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>76</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>width</string> 
+                 <int>84</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>&lt;html&gt;&lt;i&gt;Maximum depth of reads to display.</string> 
+                </void> 
+                <void property="name"> 
+                 <string>jLabel14</string> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>296</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>40</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>width</string> 
+                 <int>390</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>height</string> 
+                 <int>30</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JTextField</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>0</string> 
+                </void> 
+                <void property="name"> 
+                 <string>insertSizeThresholdField</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>insertSizeThresholdFieldActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.FocusListener</string> 
+                  <string>focusLost</string> 
+                  <string>insertSizeThresholdFieldFocusLost</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>206</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>110</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>width</string> 
+                 <int>84</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Maximum read depth:</string> 
+                </void> 
+                <void property="name"> 
+                 <string>jLabel13</string> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>6</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>47</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Mapping quality threshold:</string> 
+                </void> 
+                <void property="name"> 
+                 <string>jLabel15</string> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>6</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>82</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>&lt;html&gt;&lt;i&gt;Paired end alignments with insert sizes &gt; this value are flagged.</string> 
+                </void> 
+                <void property="name"> 
+                 <string>jLabel18</string> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>296</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>110</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>width</string> 
+                 <int>414</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>height</string> 
+                 <int>40</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JTextField</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>jTextField1</string> 
+                </void> 
+                <void property="name"> 
+                 <string>samMaxWindowSizeField</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>samMaxWindowSizeFieldActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.FocusListener</string> 
+                  <string>focusLost</string> 
+                  <string>samMaxWindowSizeFieldFocusLost</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>206</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>6</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>&lt;html&gt;&lt;i&gt;Nominal window size at which alignments become visible</string> 
+                </void> 
+                <void property="name"> 
+                 <string>jLabel12</string> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>296</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>12</int> 
+                </void> 
+               </object> 
+              </void> 
+             </object> 
+             <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+              <class>com.jformdesigner.runtime.NullConstraints</class> 
+              <void method="setProperty"> 
+               <string>x</string> 
+               <int>6</int> 
+              </void> 
+              <void method="setProperty"> 
+               <string>y</string> 
+               <int>20</int> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormContainer"> 
+              <string>javax.swing.JPanel</string> 
+              <object class="com.jformdesigner.model.FormLayoutManager"> 
+               <class>com.jformdesigner.runtime.NullLayout</class> 
+              </object> 
+              <void property="name"> 
+               <string>jPanel12</string> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JTextField</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>0</string> 
+                </void> 
+                <void property="name"> 
+                 <string>samMinBaseQualityField</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>samMinBaseQualityFieldActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.FocusListener</string> 
+                  <string>focusLost</string> 
+                  <string>samMinBaseQualityFieldFocusLost</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>325</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>140</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>width</string> 
+                 <int>50</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JCheckBox</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Shade mismatched bases by quality. </string> 
+                </void> 
+                <void property="name"> 
+                 <string>samShadeMismatchedBaseCB</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>samShadeMismatchedBaseCBActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>6</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>146</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>width</string> 
+                 <int>290</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JTextField</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>0</string> 
+                </void> 
+                <void property="name"> 
+                 <string>samMaxBaseQualityField</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>samMaxBaseQualityFieldActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.FocusListener</string> 
+                  <string>focusLost</string> 
+                  <string>samMaxBaseQualityFieldFocusLost</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>455</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>140</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>width</string> 
+                 <int>50</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JCheckBox</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Show coverage track</string> 
+                </void> 
+                <void property="name"> 
+                 <string>showCovTrackCB</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>showCovTrackCBActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>385</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>76</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>width</string> 
+                 <int>270</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JCheckBox</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Filter duplicate reads</string> 
+                </void> 
+                <void property="name"> 
+                 <string>samFilterDuplicatesCB</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>samShowDuplicatesCBActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>6</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>6</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>width</string> 
+                 <int>290</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JCheckBox</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Show reference sequence</string> 
+                </void> 
+                <void property="name"> 
+                 <string>showRefSeqCB</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>showRefSeqCBActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>385</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>6</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>width</string> 
+                 <int>270</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Min: </string> 
+                </void> 
+                <void property="name"> 
+                 <string>jLabel19</string> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>285</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>145</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JCheckBox</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Filter alignments</string> 
+                </void> 
+                <void property="name"> 
+                 <string>filterCB</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>filterCBActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>6</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>181</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>width</string> 
+                 <int>144</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JTextField</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>URL or path to filter file</string> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>enabled</string> 
+                 <boolean>false</boolean> 
+                </void> 
+                <void property="name"> 
+                 <string>filterURL</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>filterURLActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.FocusListener</string> 
+                  <string>focusLost</string> 
+                  <string>filterURLFocusLost</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>190</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>185</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>width</string> 
+                 <int>482</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JCheckBox</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Flag unmapped pairs</string> 
+                </void> 
+                <void property="name"> 
+                 <string>samFlagUnmappedPairCB</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>samFlagUnmappedPairCBActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>6</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>76</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>width</string> 
+                 <int>310</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JCheckBox</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Shade alignments intersecting center</string> 
+                </void> 
+                <void property="name"> 
+                 <string>shadeCenterCB</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>shadeCenterCBActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>385</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>41</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>width</string> 
+                 <int>450</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>&lt;html&gt;Chromosome color legend &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;i&gt;Used to flag paired end reads with mates on other chromosomes</string> 
+                </void> 
+                <void property="name"> 
+                 <string>jLabel10</string> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>5</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>230</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>width</string> 
+                 <int>640</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>height</string> 
+                 <int>30</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormContainer"> 
+                <string>javax.swing.JPanel</string> 
+                <object class="com.jformdesigner.model.FormLayoutManager"> 
+                 <class>org.jdesktop.layout.GroupLayout</class> 
+                 <void method="setProperty"> 
+                  <string>$horizontalGroup</string> 
+                  <string>par l {space :0:602:x}</string> 
+                 </void> 
+                 <void method="setProperty"> 
+                  <string>$verticalGroup</string> 
+                  <string>par l {space :0:25:x}</string> 
+                 </void> 
+                </object> 
+                <void method="auxiliary"> 
+                 <void method="setProperty"> 
+                  <string>JavaCodeGenerator.customCreateCode</string> 
+                  <string>new ChromosomeColorLegend();</string> 
+                 </void> 
+                </void> 
+                <void property="name"> 
+                 <string>legendPanel</string> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>5</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>270</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JCheckBox</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Filter vendor failed reads</string> 
+                </void> 
+                <void property="name"> 
+                 <string>filterFailedReadsCB</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>filterVendorFailedReadsCBActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>6</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>41</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Max:</string> 
+                </void> 
+                <void property="name"> 
+                 <string>label2</string> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>400</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>145</int> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JCheckBox</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Show soft-clipped bases</string> 
+                </void> 
+                <void property="name"> 
+                 <string>showSoftClippedCB</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>showSoftClippedCBActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+               <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+                <class>com.jformdesigner.runtime.NullConstraints</class> 
+                <void method="setProperty"> 
+                 <string>x</string> 
+                 <int>6</int> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>y</string> 
+                 <int>111</int> 
+                </void> 
+               </object> 
+              </void> 
+             </object> 
+             <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+              <class>com.jformdesigner.runtime.NullConstraints</class> 
+              <void method="setProperty"> 
+               <string>x</string> 
+               <int>6</int> 
+              </void> 
+              <void method="setProperty"> 
+               <string>y</string> 
+               <int>198</int> 
+              </void> 
+              <void method="setProperty"> 
+               <string>width</string> 
+               <int>740</int> 
+              </void> 
+              <void method="setProperty"> 
+               <string>height</string> 
+               <int>297</int> 
+              </void> 
+             </object> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.NullConstraints</class> 
+            <void method="setProperty"> 
+             <string>width</string> 
+             <int>760</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>height</string> 
+             <int>510</int> 
+            </void> 
+           </object> 
+          </void> 
+         </object> 
+         <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+          <null/> 
+          <void method="setProperty"> 
+           <string>title</string> 
+           <string>Alignments</string> 
+          </void> 
+         </object> 
+        </void> 
+        <void method="add"> 
+         <object class="com.jformdesigner.model.FormContainer"> 
+          <string>javax.swing.JPanel</string> 
+          <object class="com.jformdesigner.model.FormLayoutManager"> 
+           <class>com.jformdesigner.runtime.NullLayout</class> 
+          </object> 
+          <void property="name"> 
+           <string>expressionPane</string> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormContainer"> 
+            <string>javax.swing.JPanel</string> 
+            <object class="com.jformdesigner.model.FormLayoutManager"> 
+             <class>org.jdesktop.layout.GroupLayout</class> 
+             <void method="setProperty"> 
+              <string>$horizontalGroup</string> 
+              <string>par l {seq {par l {seq l {space :p:45:p, par l {comp expMapToLociCB::l:p::p, comp expMapToGeneCB::l:p::p}}, seq l {space :::p, par l {seq {space :24:24:24, comp jLabel21:::p:497:p}, comp jLabel24::l:p::p}}}, space ::193:x}}</string> 
+             </void> 
+             <void method="setProperty"> 
+              <string>$verticalGroup</string> 
+              <string>par l {seq l {space :::p, comp jLabel24:::p::p, space :::p, comp jLabel21:::p:44:p, space :::p, comp expMapToLociCB:::p::p, space :p:14:p, comp expMapToGeneCB:::p::p, space ::172:x}}</string> 
+             </void> 
+            </object> 
+            <void property="name"> 
+             <string>jPanel8</string> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JRadioButton</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Map probes to genes</string> 
+              </void> 
+              <void property="name"> 
+               <string>expMapToGeneCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>expMapToGeneCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Expression probe mapping options: </string> 
+              </void> 
+              <void property="name"> 
+               <string>jLabel24</string> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JRadioButton</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>&lt;html&gt;Map probes to target loci</string> 
+              </void> 
+              <void property="name"> 
+               <string>expMapToLociCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>expMapToLociCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>&lt;html&gt;&lt;i&gt;Note: Changes will not affect currently loaded datasets.</string> 
+              </void> 
+              <void property="name"> 
+               <string>jLabel21</string> 
+              </void> 
+             </object> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.NullConstraints</class> 
+            <void method="setProperty"> 
+             <string>x</string> 
+             <int>10</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>y</string> 
+             <int>30</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>width</string> 
+             <int>720</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>height</string> 
+             <int>310</int> 
+            </void> 
+           </object> 
+          </void> 
+         </object> 
+         <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+          <null/> 
+          <void method="setProperty"> 
+           <string>title</string> 
+           <string>Probes</string> 
+          </void> 
+         </object> 
+        </void> 
+        <void method="add"> 
+         <object class="com.jformdesigner.model.FormContainer"> 
+          <string>javax.swing.JPanel</string> 
+          <object class="com.jformdesigner.model.FormLayoutManager"> 
+           <class>com.jformdesigner.runtime.NullLayout</class> 
+          </object> 
+          <void method="setProperty"> 
+           <string>border</string> 
+           <object class="javax.swing.border.EmptyBorder"> 
+            <object class="java.awt.Insets"> 
+             <int>1</int> 
+             <int>10</int> 
+             <int>1</int> 
+             <int>10</int> 
+            </object> 
+           </object> 
+          </void> 
+          <void property="name"> 
+           <string>advancedPanel</string> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormContainer"> 
+            <string>javax.swing.JPanel</string> 
+            <object class="com.jformdesigner.model.FormLayoutManager"> 
+             <class>org.jdesktop.layout.GroupLayout</class> 
+             <void method="setProperty"> 
+              <string>$horizontalGroup</string> 
+              <string>par l {seq {space :::p, par l {comp jPanel7::l:::x, comp jPanel2::l:p::p}, space :::p}}</string> 
+             </void> 
+             <void method="setProperty"> 
+              <string>$verticalGroup</string> 
+              <string>par l {seq l {space :p:20:p, comp jPanel7:::p::p, space ::58:x, comp jPanel2:::p::p, space :::p}}</string> 
+             </void> 
+            </object> 
+            <void property="name"> 
+             <string>jPanel3</string> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormContainer"> 
+              <string>javax.swing.JPanel</string> 
+              <object class="com.jformdesigner.model.FormLayoutManager"> 
+               <class>org.jdesktop.layout.GroupLayout</class> 
+               <void method="setProperty"> 
+                <string>$horizontalGroup</string> 
+                <string>par l {seq {space :::p, par l {seq l {space :p:29:p, par l {comp jLabel1::l:p::p, seq l {comp jLabel6:::p::p, space :p:44:p, par l:::p {comp dataServerURLTextField:::::x:1, comp genomeServerURLTextField::l:p:494:p:1}}}}, seq l {comp editServerPropertiesCB:::p::p, space s:::p, comp jButton1:::p::p}, comp clearGenomeCacheButton::l:p::p}, space :::x}}</string> 
+               </void> 
+               <void method="setProperty"> 
+                <string>$verticalGroup</string> 
+                <string>par l {seq {space :::p, par b {comp editServerPropertiesCB::b:p::p, comp jButton1::b:p::p}, space :::p, par c {comp jLabel1::c:p::p, comp genomeServerURLTextField::c:p::p}, space :::p, par b {comp jLabel6::b:p::p, comp dataServerURLTextField::b:p::p}, space s:::p, comp clearGenomeCacheButton:::p::p, space :::x}}</string> 
+               </void> 
+              </object> 
+              <void property="name"> 
+               <string>jPanel2</string> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Genome Server URL</string> 
+                </void> 
+                <void property="name"> 
+                 <string>jLabel1</string> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JTextField</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>jTextField1</string> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>enabled</string> 
+                 <boolean>false</boolean> 
+                </void> 
+                <void property="name"> 
+                 <string>genomeServerURLTextField</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>genomeServerURLTextFieldActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.FocusListener</string> 
+                  <string>focusLost</string> 
+                  <string>genomeServerURLTextFieldFocusLost</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Data Registry URL</string> 
+                </void> 
+                <void property="name"> 
+                 <string>jLabel6</string> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JTextField</string> 
+                <void method="setProperty"> 
+                 <string>enabled</string> 
+                 <boolean>false</boolean> 
+                </void> 
+                <void property="name"> 
+                 <string>dataServerURLTextField</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>dataServerURLTextFieldActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.FocusListener</string> 
+                  <string>focusLost</string> 
+                  <string>dataServerURLTextFieldFocusLost</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JCheckBox</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Edit server properties</string> 
+                </void> 
+                <void property="name"> 
+                 <string>editServerPropertiesCB</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>editServerPropertiesCBActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JButton</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Clear Genome Cache</string> 
+                </void> 
+                <void property="name"> 
+                 <string>clearGenomeCacheButton</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>clearGenomeCacheButtonActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JButton</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Reset to Defaults</string> 
+                </void> 
+                <void property="name"> 
+                 <string>jButton1</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>jButton1ActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormContainer"> 
+              <string>javax.swing.JPanel</string> 
+              <object class="com.jformdesigner.model.FormLayoutManager"> 
+               <class>org.jdesktop.layout.GroupLayout</class> 
+               <void method="setProperty"> 
+                <string>$horizontalGroup</string> 
+                <string>par l {seq {par l {seq l {space :::p, comp enablePortCB:::p::p, space :p:39:p, comp portField:::p:126:p}, seq l {space :p:48:p, comp jLabel22:::p::p}}, space ::330:x}}</string> 
+               </void> 
+               <void method="setProperty"> 
+                <string>$verticalGroup</string> 
+                <string>par l {seq l {space :p:28:p, par c {comp enablePortCB::c:p::p, comp portField::c:p::p}, space u:::p, comp jLabel22:::p::p, space :::x}}</string> 
+               </void> 
+              </object> 
+              <void property="name"> 
+               <string>jPanel7</string> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JCheckBox</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Enable port</string> 
+                </void> 
+                <void property="name"> 
+                 <string>enablePortCB</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>enablePortCBActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JTextField</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>60151</string> 
+                </void> 
+                <void property="name"> 
+                 <string>portField</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>portFieldActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.FocusListener</string> 
+                  <string>focusLost</string> 
+                  <string>portFieldFocusLost</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>font</string> 
+                 <object class="java.awt.Font"> 
+                  <string>Lucida Grande</string> 
+                  <int>2</int> 
+                  <int>13</int> 
+                 </object> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Enable port to send commands and http requests to IGV. </string> 
+                </void> 
+                <void property="name"> 
+                 <string>jLabel22</string> 
+                </void> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.NullConstraints</class> 
+            <void method="setProperty"> 
+             <string>x</string> 
+             <int>10</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>width</string> 
+             <int>750</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>height</string> 
+             <int>330</int> 
+            </void> 
+           </object> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormContainer"> 
+            <string>javax.swing.JPanel</string> 
+            <object class="com.jformdesigner.model.FormLayoutManager"> 
+             <class>org.jdesktop.layout.GroupLayout</class> 
+             <void method="setProperty"> 
+              <string>$horizontalGroup</string> 
+              <string>par l {seq {par l {seq l {space :p:44:p, comp jLabel25:::p:601:p}, seq l {space :::p, comp useByteRangeCB:::p::p}}, space ::65:x}}</string> 
+             </void> 
+             <void method="setProperty"> 
+              <string>$verticalGroup</string> 
+              <string>par l {seq l {space :::p, comp useByteRangeCB:::p:38:p, space :::p, comp jLabel25::::64:x, space :p:6:p}}</string> 
+             </void> 
+            </object> 
+            <void property="name"> 
+             <string>jPanel9</string> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JCheckBox</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Use http byte-range requests</string> 
+              </void> 
+              <void property="name"> 
+               <string>useByteRangeCB</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>useByteRangeCBActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>font</string> 
+               <object class="java.awt.Font"> 
+                <string>Lucida Grande</string> 
+                <int>2</int> 
+                <int>13</int> 
+               </object> 
+              </void> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>&lt;html&gt;This option applies to certain &quot;Load from Server...&quot; tracks hosted at the Broad.    Disable this option if you are unable to load the phastCons conservation track under the hg18 annotations.</string> 
+              </void> 
+              <void property="name"> 
+               <string>jLabel25</string> 
+              </void> 
+             </object> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.NullConstraints</class> 
+            <void method="setProperty"> 
+             <string>x</string> 
+             <int>30</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>y</string> 
+             <int>340</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>width</string> 
+             <int>710</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>height</string> 
+             <int>120</int> 
+            </void> 
+           </object> 
+          </void> 
+         </object> 
+         <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+          <null/> 
+          <void method="setProperty"> 
+           <string>title</string> 
+           <string>Advanced</string> 
+          </void> 
+         </object> 
+        </void> 
+        <void method="add"> 
+         <object class="com.jformdesigner.model.FormContainer"> 
+          <string>javax.swing.JPanel</string> 
+          <object class="com.jformdesigner.model.FormLayoutManager"> 
+           <class>javax.swing.BoxLayout</class> 
+          </object> 
+          <void property="name"> 
+           <string>proxyPanel</string> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormContainer"> 
+            <string>javax.swing.JPanel</string> 
+            <object class="com.jformdesigner.model.FormLayoutManager"> 
+             <class>org.jdesktop.layout.GroupLayout</class> 
+             <void method="setProperty"> 
+              <string>$horizontalGroup</string> 
+              <string>par l {seq {space :::p, par l {comp jPanel17::l:p::p:1, seq l {comp jPanel16:::::x:1, space :33:33:p}, seq l {comp label3:::p:674:p, space :::p}}}, seq l {space :p:17:p, comp clearAllProxyButton:::p::p, space ::667:x}}</string> 
+             </void> 
+             <void method="setProperty"> 
+              <string>$verticalGroup</string> 
+              <string>par l {seq {comp label3:::p:82:p, space :::p, comp jPanel17:::p::p, space s:::p, comp jPanel16:::::x, space :p:32:p, comp clearAllProxyButton:::p::p, space :p:87:p}}</string> 
+             </void> 
+            </object> 
+            <void property="name"> 
+             <string>jPanel15</string> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormContainer"> 
+              <string>javax.swing.JPanel</string> 
+              <object class="com.jformdesigner.model.FormLayoutManager"> 
+               <class>org.jdesktop.layout.GroupLayout</class> 
+               <void method="setProperty"> 
+                <string>$horizontalGroup</string> 
+                <string>par l {seq {par l {seq {space :p:28:p, par l {comp jLabel28::l:p::p, comp jLabel29::l:p::p}, space :p:37:p, par l:::p {comp proxyPasswordField:::::x:1, comp proxyUsernameField::l::261:x:1}}, seq l {space :::p, comp authenticateProxyCB:::p::p}}, space ::354:x}}</string> 
+               </void> 
+               <void method="setProperty"> 
+                <string>$verticalGroup</string> 
+                <string>par l {seq {space :p:17:p, comp authenticateProxyCB:::p::p, space :::p, par b {comp jLabel28::b:p::p, comp proxyUsernameField::b:p::p}, space :::p, par b {comp jLabel29::b:p::p, comp proxyPasswordField::b:p::p}, space :::x}}</string> 
+               </void> 
+              </object> 
+              <void property="name"> 
+               <string>jPanel16</string> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JTextField</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>jTextField1</string> 
+                </void> 
+                <void method="setProperty"> 
+                 <string>enabled</string> 
+                 <boolean>false</boolean> 
+                </void> 
+                <void property="name"> 
+                 <string>proxyUsernameField</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>proxyUsernameFieldActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.FocusListener</string> 
+                  <string>focusLost</string> 
+                  <string>proxyUsernameFieldFocusLost</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Username</string> 
+                </void> 
+                <void property="name"> 
+                 <string>jLabel28</string> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JCheckBox</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Authentication required</string> 
+                </void> 
+                <void property="name"> 
+                 <string>authenticateProxyCB</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>authenticateProxyCBActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Password</string> 
+                </void> 
+                <void property="name"> 
+                 <string>jLabel29</string> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JPasswordField</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>jPasswordField1</string> 
+                </void> 
+                <void property="name"> 
+                 <string>proxyPasswordField</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.FocusListener</string> 
+                  <string>focusLost</string> 
+                  <string>proxyPasswordFieldFocusLost</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormContainer"> 
+              <string>javax.swing.JPanel</string> 
+              <object class="com.jformdesigner.model.FormLayoutManager"> 
+               <class>org.jdesktop.layout.GroupLayout</class> 
+               <void method="setProperty"> 
+                <string>$horizontalGroup</string> 
+                <string>par l {seq {par l {seq {space :::p, par l {comp jLabel27::l:p::p, comp jLabel23::l:p::p}, space :p:28:p, par l {comp proxyPortField:::p:108:p, comp proxyHostField::l:p:485:p}}, seq l {space :p:9:p, comp useProxyCB:::p::p}}, space ::21:x}}</string> 
+               </void> 
+               <void method="setProperty"> 
+                <string>$verticalGroup</string> 
+                <string>par l {seq t {space ::29:x, comp useProxyCB:::p::p, space s:::p, par b {comp jLabel23::b:p::p, comp proxyHostField::b:p::p}, space :::p, par b {comp jLabel27::b:p::p, comp proxyPortField::b:p::p}, space :::p}}</string> 
+               </void> 
+              </object> 
+              <void property="name"> 
+               <string>jPanel17</string> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JTextField</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>jTextField1</string> 
+                </void> 
+                <void property="name"> 
+                 <string>proxyHostField</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>proxyHostFieldActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.FocusListener</string> 
+                  <string>focusLost</string> 
+                  <string>proxyHostFieldFocusLost</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JTextField</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>jTextField1</string> 
+                </void> 
+                <void property="name"> 
+                 <string>proxyPortField</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>proxyPortFieldActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.FocusListener</string> 
+                  <string>focusLost</string> 
+                  <string>proxyPortFieldFocusLost</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Proxy port</string> 
+                </void> 
+                <void property="name"> 
+                 <string>jLabel27</string> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JLabel</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Proxy host</string> 
+                </void> 
+                <void property="name"> 
+                 <string>jLabel23</string> 
+                </void> 
+               </object> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JCheckBox</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>Use proxy</string> 
+                </void> 
+                <void property="name"> 
+                 <string>useProxyCB</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>useProxyCBActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JLabel</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>&lt;html&gt;Note:  do not use these settings unless you receive error or warning messages about server connections.  On most systems the correct settings will be automatically copied from your web browser.</string> 
+              </void> 
+              <void property="name"> 
+               <string>label3</string> 
+              </void> 
+             </object> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JButton</string> 
+              <void method="setProperty"> 
+               <string>text</string> 
+               <string>Clear All</string> 
+              </void> 
+              <void property="name"> 
+               <string>clearAllProxyButton</string> 
+              </void> 
+              <void method="addEvent"> 
+               <object class="com.jformdesigner.model.FormEvent"> 
+                <string>java.awt.event.ActionListener</string> 
+                <string>actionPerformed</string> 
+                <string>clearAllProxyButtonActionPerformed</string> 
+                <boolean>true</boolean> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+           </object> 
+          </void> 
+         </object> 
+         <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+          <null/> 
+          <void method="setProperty"> 
+           <string>title</string> 
+           <string>Proxy</string> 
+          </void> 
+         </object> 
+        </void> 
+       </object> 
+       <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+        <class>java.lang.String</class> 
+        <void method="setProperty"> 
+         <string>value</string> 
+         <string>Center</string> 
+        </void> 
+       </object> 
+      </void> 
+      <void method="add"> 
+       <object class="com.jformdesigner.model.FormContainer"> 
+        <string>com.jidesoft.dialog.ButtonPanel</string> 
+        <null/> 
+        <void property="name"> 
+         <string>okCancelButtonPanel</string> 
+        </void> 
+        <void method="add"> 
+         <object class="com.jformdesigner.model.FormComponent"> 
+          <string>javax.swing.JButton</string> 
+          <void method="setProperty"> 
+           <string>text</string> 
+           <string>OK</string> 
+          </void> 
+          <void property="name"> 
+           <string>okButton</string> 
+          </void> 
+          <void method="addEvent"> 
+           <object class="com.jformdesigner.model.FormEvent"> 
+            <string>java.awt.event.ActionListener</string> 
+            <string>actionPerformed</string> 
+            <string>okButtonActionPerformed</string> 
+            <boolean>true</boolean> 
+           </object> 
+          </void> 
+         </object> 
+        </void> 
+        <void method="add"> 
+         <object class="com.jformdesigner.model.FormComponent"> 
+          <string>javax.swing.JButton</string> 
+          <void method="setProperty"> 
+           <string>text</string> 
+           <string>Cancel</string> 
+          </void> 
+          <void property="name"> 
+           <string>cancelButton</string> 
+          </void> 
+          <void method="addEvent"> 
+           <object class="com.jformdesigner.model.FormEvent"> 
+            <string>java.awt.event.ActionListener</string> 
+            <string>actionPerformed</string> 
+            <string>cancelButtonActionPerformed</string> 
+            <boolean>true</boolean> 
+           </object> 
+          </void> 
+         </object> 
+        </void> 
+       </object> 
+       <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+        <class>java.lang.String</class> 
+        <void method="setProperty"> 
+         <string>value</string> 
+         <string>South</string> 
+        </void> 
+       </object> 
+      </void> 
+      <void property="name"> 
+       <string>this</string> 
+      </void> 
+     </object> 
+     <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+      <null/> 
+      <void method="setProperty"> 
+       <string>size</string> 
+       <object class="java.awt.Dimension"> 
+        <int>804</int> 
+        <int>605</int> 
+       </object> 
+      </void> 
+      <void method="setProperty"> 
+       <string>location</string> 
+       <object class="java.awt.Point"> 
+        <int>0</int> 
+        <int>0</int> 
+       </object> 
+      </void> 
+     </object> 
+    </void> 
+   </object> 
+  </void> 
+ </object> 
+</java> 
diff --git a/src/org/broad/igv/ui/PreprocessorDialog.form b/src/org/broad/igv/ui/PreprocessorDialog.form
deleted file mode 100644
index 95c67c4..0000000
--- a/src/org/broad/igv/ui/PreprocessorDialog.form
+++ /dev/null
@@ -1,192 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-
-<Form version="1.3" maxVersion="1.5" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
-  <Properties>
-    <Property name="defaultCloseOperation" type="int" value="2"/>
-    <Property name="title" type="java.lang.String" value="Preprocess Datasets"/>
-  </Properties>
-  <SyntheticProperties>
-    <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
-  </SyntheticProperties>
-  <AuxValues>
-    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
-    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
-    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="2"/>
-    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
-    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
-  </AuxValues>
-
-  <Layout>
-    <DimensionLayout dim="0">
-      <Group type="103" groupAlignment="0" attributes="0">
-          <Group type="102" attributes="0">
-              <Group type="103" groupAlignment="0" attributes="0">
-                  <Group type="102" alignment="1" attributes="0">
-                      <EmptySpace max="-2" attributes="0"/>
-                      <Component id="okButton" min="-2" max="-2" attributes="0"/>
-                      <EmptySpace min="-2" pref="24" max="-2" attributes="0"/>
-                      <Component id="cancelButton" min="-2" max="-2" attributes="0"/>
-                  </Group>
-                  <Group type="102" alignment="1" attributes="0">
-                      <EmptySpace max="-2" attributes="0"/>
-                      <Component id="missingDataExplanation" min="-2" max="-2" attributes="0"/>
-                  </Group>
-                  <Group type="102" alignment="1" attributes="0">
-                      <EmptySpace min="-2" pref="37" max="-2" attributes="0"/>
-                      <Group type="103" groupAlignment="0" attributes="0">
-                          <Component id="jLabel3" min="-2" max="-2" attributes="0"/>
-                          <Component id="jLabel2" min="-2" max="-2" attributes="0"/>
-                          <Component id="jLabel1" min="-2" max="-2" attributes="0"/>
-                          <Component id="jLabel4" min="-2" max="-2" attributes="0"/>
-                      </Group>
-                      <EmptySpace min="-2" pref="39" max="-2" attributes="0"/>
-                      <Group type="103" groupAlignment="1" attributes="0">
-                          <Component id="inputFileControl" pref="331" max="32767" attributes="1"/>
-                          <Component id="genomeComboBox" alignment="0" min="-2" pref="171" max="-2" attributes="0"/>
-                          <Component id="outputFileControl" alignment="0" pref="331" max="32767" attributes="1"/>
-                          <Component id="dataTypeComboBox" alignment="0" pref="331" max="32767" attributes="0"/>
-                      </Group>
-                  </Group>
-                  <Group type="102" alignment="0" attributes="0">
-                      <EmptySpace max="-2" attributes="0"/>
-                      <Component id="progressBar" pref="459" max="32767" attributes="1"/>
-                  </Group>
-              </Group>
-              <EmptySpace min="35" pref="35" max="-2" attributes="0"/>
-          </Group>
-      </Group>
-    </DimensionLayout>
-    <DimensionLayout dim="1">
-      <Group type="103" groupAlignment="0" attributes="0">
-          <Group type="102" alignment="1" attributes="0">
-              <EmptySpace max="-2" attributes="0"/>
-              <Component id="missingDataExplanation" min="-2" max="-2" attributes="0"/>
-              <EmptySpace min="-2" pref="44" max="-2" attributes="0"/>
-              <Group type="103" groupAlignment="3" attributes="0">
-                  <Component id="dataTypeComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
-                  <Component id="jLabel4" alignment="3" min="-2" max="-2" attributes="1"/>
-              </Group>
-              <EmptySpace min="-2" pref="28" max="-2" attributes="0"/>
-              <Group type="103" groupAlignment="1" attributes="0">
-                  <Group type="102" attributes="0">
-                      <Component id="inputFileControl" pref="22" max="32767" attributes="1"/>
-                      <EmptySpace min="-2" pref="27" max="-2" attributes="0"/>
-                      <Component id="outputFileControl" pref="22" max="32767" attributes="1"/>
-                  </Group>
-                  <Group type="102" attributes="0">
-                      <Component id="jLabel1" min="-2" max="-2" attributes="1"/>
-                      <EmptySpace pref="39" max="32767" attributes="0"/>
-                      <Component id="jLabel2" min="-2" max="-2" attributes="1"/>
-                  </Group>
-              </Group>
-              <EmptySpace min="-2" pref="27" max="-2" attributes="0"/>
-              <Group type="103" groupAlignment="3" attributes="0">
-                  <Component id="genomeComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
-                  <Component id="jLabel3" alignment="3" min="-2" max="-2" attributes="0"/>
-              </Group>
-              <EmptySpace min="-2" pref="39" max="-2" attributes="0"/>
-              <Group type="103" groupAlignment="3" attributes="0">
-                  <Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/>
-                  <Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
-              </Group>
-              <EmptySpace min="-2" pref="35" max="-2" attributes="0"/>
-              <Component id="progressBar" min="-2" max="-2" attributes="0"/>
-              <EmptySpace min="-2" pref="38" max="-2" attributes="0"/>
-          </Group>
-      </Group>
-    </DimensionLayout>
-  </Layout>
-  <SubComponents>
-    <Component class="com.jidesoft.combobox.FileChooserComboBox" name="inputFileControl">
-      <Events>
-        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="inputFileControlActionPerformed"/>
-      </Events>
-      <AuxValues>
-        <AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new PreprocessorDialog.PreprocessorFileChooserComboBox(JFileChooser.OPEN_DIALOG);"/>
-      </AuxValues>
-    </Component>
-    <Component class="javax.swing.JLabel" name="jLabel1">
-      <Properties>
-        <Property name="text" type="java.lang.String" value="Input File"/>
-      </Properties>
-    </Component>
-    <Component class="com.jidesoft.combobox.FileChooserComboBox" name="outputFileControl">
-      <Events>
-        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="outputFileControlActionPerformed"/>
-      </Events>
-      <AuxValues>
-        <AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new PreprocessorDialog.PreprocessorFileChooserComboBox(JFileChooser.SAVE_DIALOG);"/>
-      </AuxValues>
-    </Component>
-    <Component class="javax.swing.JLabel" name="jLabel2">
-      <Properties>
-        <Property name="text" type="java.lang.String" value="Output File"/>
-      </Properties>
-    </Component>
-    <Component class="javax.swing.JComboBox" name="genomeComboBox">
-      <Properties>
-        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
-          <Connection code="new javax.swing.DefaultComboBoxModel(getAllGenomes())" type="code"/>
-        </Property>
-      </Properties>
-    </Component>
-    <Component class="javax.swing.JLabel" name="jLabel3">
-      <Properties>
-        <Property name="text" type="java.lang.String" value="Genome"/>
-      </Properties>
-    </Component>
-    <Component class="javax.swing.JButton" name="okButton">
-      <Properties>
-        <Property name="text" type="java.lang.String" value="Run"/>
-      </Properties>
-      <Events>
-        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="okButtonActionPerformed"/>
-      </Events>
-    </Component>
-    <Component class="javax.swing.JButton" name="cancelButton">
-      <Properties>
-        <Property name="text" type="java.lang.String" value="Cancel"/>
-      </Properties>
-      <Events>
-        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/>
-      </Events>
-    </Component>
-    <Component class="javax.swing.JProgressBar" name="progressBar">
-    </Component>
-    <Component class="javax.swing.JLabel" name="missingDataExplanation">
-      <Properties>
-        <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-          <Font name="Lucida Grande" size="12" style="2"/>
-        </Property>
-        <Property name="text" type="java.lang.String" value="&lt;html&gt;Use this form to preprocess gct, res, cn, xcn, and snp files. &lt;br&gt;Preprocessing can significantly improve load times and overall performance." noResource="true"/>
-      </Properties>
-    </Component>
-    <Component class="javax.swing.JComboBox" name="dataTypeComboBox">
-      <Properties>
-        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
-          <StringArray count="7">
-            <StringItem index="0" value=""/>
-            <StringItem index="1" value="GENE_EXPRESSION"/>
-            <StringItem index="2" value="COPY_NUMBER"/>
-            <StringItem index="3" value="ALLELE_SPECIFIC_COPY_NUMBER"/>
-            <StringItem index="4" value="LOH"/>
-            <StringItem index="5" value="DNA_METHYLATION"/>
-            <StringItem index="6" value="OTHER"/>
-          </StringArray>
-        </Property>
-      </Properties>
-      <Events>
-        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="dataTypeComboBoxActionPerformed"/>
-      </Events>
-    </Component>
-    <Component class="javax.swing.JLabel" name="jLabel4">
-      <Properties>
-        <Property name="text" type="java.lang.String" value="Data Type"/>
-      </Properties>
-    </Component>
-  </SubComponents>
-</Form>
diff --git a/src/org/broad/igv/ui/PreprocessorDialog.java b/src/org/broad/igv/ui/PreprocessorDialog.java
deleted file mode 100644
index c149140..0000000
--- a/src/org/broad/igv/ui/PreprocessorDialog.java
+++ /dev/null
@@ -1,522 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * PreprocessorDialog.java
- *
- * Created on June 3, 2008, 5:42 PM
- */
-package org.broad.igv.ui;
-
-import com.jidesoft.combobox.FileChooserComboBox;
-import com.jidesoft.dialog.JideOptionPane;
-import com.jidesoft.utils.SwingWorker;
-import org.broad.igv.PreferenceManager;
-import org.broad.igv.data.*;
-import org.broad.igv.feature.GenomeDescriptor;
-import org.broad.igv.feature.GenomeManager;
-import org.broad.igv.preprocess.old.AbstractProcessor;
-import org.broad.igv.preprocess.old.NonOverlappingProcessor;
-import org.broad.igv.preprocess.old.OverlappingProcessor;
-import org.broad.igv.tools.StatusMonitor;
-import org.broad.igv.track.TrackType;
-import org.broad.igv.util.ResourceLocator;
-
-import javax.swing.*;
-import java.awt.*;
-import java.io.File;
-import java.util.Map;
-
-/**
- * @author jrobinso
- */
-public class PreprocessorDialog extends javax.swing.JDialog implements StatusMonitor {
-
-    static final String[] knownExtensions = new String[]{".gct", ".res", ".cn", ".xcn",
-            ".snp", "igv", "wig", "tab"
-    };
-
-    public boolean isInterrupted() {
-        return false;
-    }
-
-    static public class PreprocessorFileChooserComboBox extends FileChooserComboBox {
-
-        int type;
-
-        public PreprocessorFileChooserComboBox(int type) {
-            this.type = type;
-        }
-
-        @Override
-        protected void customizeFileChooser(JFileChooser chooser) {
-            chooser.setDialogType(type);
-            File lastDirectoryFile =
-                    PreferenceManager.getInstance().getLastTrackDirectory();
-            if (lastDirectoryFile != null) {
-                chooser.setCurrentDirectory(lastDirectoryFile);
-            }
-            super.customizeFileChooser(chooser);
-        }
-    }
-
-    private enum State {
-
-        INIT, PROCESSING, COMPLETE
-    }
-
-    private State state = State.INIT;
-    private ProcessWorker processWorker;
-    private double percentComplete = 0.0;
-    private TrackType dataType = TrackType.OTHER;
-
-    /**
-     * Creates new form PreprocessorDialog
-     */
-    public PreprocessorDialog(java.awt.Frame parent, boolean modal) {
-        initComponents();
-        GenomeDescriptor desc = GenomeManager.getInstance().getGenomeDescriptor(IGVModel.getInstance().getViewContext().getGenomeId());
-        genomeComboBox.setSelectedItem(desc);
-        setModal(false);
-        setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
-        setLocationRelativeTo(parent);
-    }
-
-    private Object[] getAllGenomes() {
-        Map<String, GenomeDescriptor> map =
-                GenomeManager.getInstance().getGenomeDescriptorMap();
-        return map.values().toArray();
-    }
-
-    /**
-     * This method is called from within the constructor to
-     * initialize the form.
-     * WARNING: Do NOT modify this code. The content of this method is
-     * always regenerated by the Form Editor.
-     */
-    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
-    private void initComponents() {
-
-        inputFileControl = new PreprocessorDialog.PreprocessorFileChooserComboBox(JFileChooser.OPEN_DIALOG);
-        jLabel1 = new javax.swing.JLabel();
-        outputFileControl = new PreprocessorDialog.PreprocessorFileChooserComboBox(JFileChooser.SAVE_DIALOG);
-        jLabel2 = new javax.swing.JLabel();
-        genomeComboBox = new javax.swing.JComboBox();
-        jLabel3 = new javax.swing.JLabel();
-        okButton = new javax.swing.JButton();
-        cancelButton = new javax.swing.JButton();
-        progressBar = new javax.swing.JProgressBar();
-        missingDataExplanation = new javax.swing.JLabel();
-        dataTypeComboBox = new javax.swing.JComboBox();
-        jLabel4 = new javax.swing.JLabel();
-
-        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
-        setTitle("Preprocess Datasets");
-
-        inputFileControl.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                inputFileControlActionPerformed(evt);
-            }
-        });
-
-        jLabel1.setText("Input File");
-
-        outputFileControl.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                outputFileControlActionPerformed(evt);
-            }
-        });
-
-        jLabel2.setText("Output File");
-
-        genomeComboBox.setModel(new javax.swing.DefaultComboBoxModel(getAllGenomes()));
-
-        jLabel3.setText("Genome");
-
-        okButton.setText("Run");
-        okButton.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                okButtonActionPerformed(evt);
-            }
-        });
-
-        cancelButton.setText("Cancel");
-        cancelButton.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                cancelButtonActionPerformed(evt);
-            }
-        });
-
-        missingDataExplanation.setFont(new java.awt.Font("Lucida Grande", 2, 12));
-        missingDataExplanation.setText("<html>Use this form to preprocess gct, res, cn, xcn, and snp files. <br>Preprocessing can significantly improve load times and overall performance."); // NOI18N
-
-        dataTypeComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[]{"", "GENE_EXPRESSION", "COPY_NUMBER", "ALLELE_SPECIFIC_COPY_NUMBER", "LOH", "DNA_METHYLATION", "OTHER"}));
-        dataTypeComboBox.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                dataTypeComboBoxActionPerformed(evt);
-            }
-        });
-
-        jLabel4.setText("Data Type");
-
-        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
-        getContentPane().setLayout(layout);
-        layout.setHorizontalGroup(
-                layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(layout.createSequentialGroup()
-                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
-                                        .addContainerGap()
-                                        .add(okButton)
-                                        .add(24, 24, 24)
-                                        .add(cancelButton))
-                                .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
-                                        .addContainerGap()
-                                        .add(missingDataExplanation))
-                                .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
-                                        .add(37, 37, 37)
-                                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                                                .add(jLabel3)
-                                                .add(jLabel2)
-                                                .add(jLabel1)
-                                                .add(jLabel4))
-                                        .add(39, 39, 39)
-                                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
-                                        .add(inputFileControl, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 331, Short.MAX_VALUE)
-                                        .add(org.jdesktop.layout.GroupLayout.LEADING, genomeComboBox, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 171, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                        .add(org.jdesktop.layout.GroupLayout.LEADING, outputFileControl, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 331, Short.MAX_VALUE)
-                                        .add(org.jdesktop.layout.GroupLayout.LEADING, dataTypeComboBox, 0, 331, Short.MAX_VALUE)))
-                                .add(layout.createSequentialGroup()
-                                .addContainerGap()
-                                .add(progressBar, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 459, Short.MAX_VALUE)))
-                        .add(35, 35, 35))
-        );
-        layout.setVerticalGroup(
-                layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
-                        .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
-                        .addContainerGap()
-                        .add(missingDataExplanation)
-                        .add(44, 44, 44)
-                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
-                                .add(dataTypeComboBox, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                .add(jLabel4))
-                        .add(28, 28, 28)
-                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
-                                .add(layout.createSequentialGroup()
-                                        .add(inputFileControl, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 22, Short.MAX_VALUE)
-                                        .add(27, 27, 27)
-                                        .add(outputFileControl, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 22, Short.MAX_VALUE))
-                                .add(layout.createSequentialGroup()
-                                .add(jLabel1)
-                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 39, Short.MAX_VALUE)
-                                .add(jLabel2)))
-                        .add(27, 27, 27)
-                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
-                                .add(genomeComboBox, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                                .add(jLabel3))
-                        .add(39, 39, 39)
-                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
-                                .add(okButton)
-                                .add(cancelButton))
-                        .add(35, 35, 35)
-                        .add(progressBar, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
-                        .add(38, 38, 38))
-        );
-
-        pack();
-    }// </editor-fold>//GEN-END:initComponents
-
-    private void inputFileControlActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_inputFileControlActionPerformed
-        if (outputFileControl.getSelectedItem() == null && inputFileControl.getSelectedItem() != null) {
-            String inputFile = inputFileControl.getSelectedItem().toString();
-            String outputFile = inputFile + ".h5";
-            outputFileControl.setSelectedItem(new File(outputFile));
-            //datasetNameField.setText(inputFileControl.getSelectedItem().toString());
-        }
-    }//GEN-LAST:event_inputFileControlActionPerformed
-
-    private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
-
-        if (state == State.INIT && validateInput()) {
-            setState(State.PROCESSING);
-            process();
-        } else if (state == State.PROCESSING) {
-            if (processWorker != null) {
-                processWorker.cancel(true);
-            }
-            String inputFile = inputFileControl.getSelectedItem().toString();
-            if (inputFile != null) {
-                File directory = new File(inputFile).getParentFile();
-                PreferenceManager.getInstance().setLastTrackDirectory(directory);
-            }
-        } else if (state == State.COMPLETE) {
-            setVisible(false);
-        }
-
-
-    }//GEN-LAST:event_okButtonActionPerformed
-
-    private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
-
-        setVisible(false);
-    }//GEN-LAST:event_cancelButtonActionPerformed
-
-    private void outputFileControlActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_outputFileControlActionPerformed
-// TODO add your handling code here:
-    }//GEN-LAST:event_outputFileControlActionPerformed
-
-    private void dataTypeComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dataTypeComboBoxActionPerformed
-        String dataTypeString = dataTypeComboBox.getSelectedItem().toString();
-        if (dataTypeString != null && dataTypeString.length() > 0) {
-            dataType = TrackType.valueOf(dataTypeString);
-        }
-    }//GEN-LAST:event_dataTypeComboBoxActionPerformed
-
-    void setState(State state) {
-        this.state = state;
-        switch (state) {
-            case INIT:
-                setPercentComplete(0.0);
-                okButton.setText("Run");
-                okButton.setEnabled(true);
-                cancelButton.setEnabled(true);
-                break;
-            case PROCESSING:
-                cancelButton.setEnabled(false);
-                okButton.setText("Stop");
-                break;
-            case COMPLETE:
-                okButton.setText("Done");
-                okButton.setEnabled(true);
-                setPercentComplete(100.0);
-        }
-    }
-
-    void preprocessingComplete() {
-        processWorker = null;
-        GuiUtilities.invokeOnEventThread(new Runnable() {
-
-            public void run() {
-                setCursor(null);
-                setState(State.COMPLETE);
-            }
-        });
-    }
-
-    void preprocessingCanceled() {
-        setCursor(null);
-        processWorker = null;
-        GuiUtilities.invokeOnEventThread(new Runnable() {
-
-            public void run() {
-                setCursor(null);
-                setState(State.INIT);
-            }
-        });
-
-    }
-
-    private void process() {
-        processWorker = new ProcessWorker();
-        processWorker.execute();
-    }
-
-    public void setPercentComplete(final double percent) {
-
-        if (percent > 0 && percent < 1.0d) {
-            percentComplete = Math.ceil(percent);
-
-            // This line is a workaround
-            percentComplete = 3.0d; // TODO Remove this line once we figure
-            // out why the progress bar ignore values
-            // less than 3.0
-        } else {
-            this.percentComplete = percent;
-        }
-
-        GuiUtilities.invokeOnEventThread(new Runnable() {
-
-            public void run() {
-                progressBar.setValue((int) percentComplete);
-                progressBar.updateUI();
-                Rectangle progressRect = progressBar.getBounds();
-                progressBar.paintImmediately(progressRect);
-            }
-        });
-
-        //System.out.println("Percent Complete: "+this.percent);
-    }
-
-    public void incrementPercentComplete(final double increment) {
-        if (progressBar.getValue() + increment >= 100) {
-            System.out.println("Done");
-        }
-        setPercentComplete(percentComplete + increment);
-
-    }
-
-    private boolean validateInput() {
-
-        if (inputFileControl.getSelectedItem() == null ||
-                outputFileControl.getSelectedItem() == null ||
-                genomeComboBox.getSelectedItem() == null) {
-            JideOptionPane.showMessageDialog(this, "All fields are required");
-            return false;
-        }
-
-        // Check file extension
-        String inputFile = inputFileControl.getSelectedItem().toString();
-        String tmp = (inputFile.endsWith(".txt") ? inputFile.substring(0, inputFile.length() - 4) : inputFile);
-        boolean validExtension = false;
-        for (String ext : knownExtensions) {
-            if (tmp.endsWith(ext)) {
-                validExtension = true;
-                break;
-            }
-        }
-        if (!validExtension) {
-            JideOptionPane.showMessageDialog(this, "Unknown file type: " + inputFile);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * @param <String>
-     * @param <Void>
-     */
-    class ProcessWorker<String, Void> extends SwingWorker {
-
-        private boolean success;
-
-        @Override
-        protected Object doInBackground() throws Exception {
-
-            try {
-                setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
-                java.lang.String inputFile = inputFileControl.getSelectedItem().toString();
-                java.lang.String outputFile = outputFileControl.getSelectedItem().toString();
-                java.lang.String genomeId = ((GenomeDescriptor) genomeComboBox.getSelectedItem()).getId();
-                java.lang.String name = inputFile;
-                if (!name.endsWith(".h5")) {
-                    name = name + ".h5";
-                }
-
-                Dataset ds = null;
-                AbstractProcessor proc = null;
-
-                java.lang.String tmp = (inputFile.endsWith(".txt") ? inputFile.substring(0, inputFile.length() - 4) : inputFile);
-                if (tmp.endsWith(".gct") || tmp.endsWith(".res") || inputFile.endsWith(".tab")) {
-                    GCTDatasetParser parser = new GCTDatasetParser(new ResourceLocator(inputFile),  null, genomeId);
-                    ds = parser.createDataset();
-                    ((GCTDataset) ds).setType(TrackType.GENE_EXPRESSION);
-                    ((GCTDataset) ds).setNormalized(true);
-                    ((GCTDataset) ds).setLogValues(true);
-                    proc = new OverlappingProcessor(ds, PreprocessorDialog.this);
-                    proc.setZoomMax(2);
-                    ds.setName((new File(inputFile)).getName().replace("/.h5", ""));
-                    success = proc.process(outputFile);
-
-                } else if (tmp.endsWith(".snp") || tmp.endsWith(".cn") || tmp.endsWith(".xcn")) {
-                    ds = new IGVDataset(genomeId, new ResourceLocator(inputFile));
-                    proc = new NonOverlappingProcessor(ds, PreprocessorDialog.this);
-                    proc.setZoomMax(3);
-                    ds.setName((new File(inputFile)).getName().replace("/.h5", ""));
-                    success = proc.process(outputFile);
-
-                } else if (tmp.endsWith(".igv")) {
-                    ds = new IGVDataset(genomeId, new ResourceLocator(inputFile));
-                    proc = new OverlappingProcessor(ds, PreprocessorDialog.this);
-                    proc.setZoomMax(3);
-                    ds.setName((new File(inputFile)).getName().replace("/.h5", ""));
-                    success = proc.process(outputFile);
-                } else if (tmp.endsWith(".wig")) {
-                    java.lang.String dsName = (new File(inputFile)).getName();
-
-                    ds = (new WiggleParser(new ResourceLocator(inputFile), genomeId)).parse();
-
-
-                    proc = new OverlappingProcessor(ds, PreprocessorDialog.this);
-                    proc.setZoomMax(4);
-                    ds.setName((new File(inputFile)).getName().replace("/.h5", ""));
-                    success = proc.process(outputFile);
-
-                } else {
-                    return false;
-                }
-
-            } catch (Exception e) {
-                e.printStackTrace();
-                JOptionPane.showMessageDialog(PreprocessorDialog.this, "Processing error: " + e.getMessage());
-            } finally {
-                setCursor(null);
-            }
-
-            return success;
-
-        }
-
-        @Override
-        protected void done() {
-            // Update the UI.  Must invoke update code with SwingUtilities since
-            // this is not running on the swing thread.
-            if (success) {
-                preprocessingComplete();
-            } else {
-                preprocessingCanceled();
-            }
-
-        }
-    }
-
-    /**
-     * @param args the command line arguments
-     */
-    public static void main(String args[]) {
-        java.awt.EventQueue.invokeLater(new Runnable() {
-
-            public void run() {
-                PreprocessorDialog dialog = new PreprocessorDialog(new javax.swing.JFrame(), true);
-                dialog.addWindowListener(new java.awt.event.WindowAdapter() {
-
-                    @Override
-                    public void windowClosing(java.awt.event.WindowEvent e) {
-                        System.exit(0);
-                    }
-                });
-                dialog.setVisible(true);
-            }
-        });
-    }
-
-    // Variables declaration - do not modify//GEN-BEGIN:variables
-    private javax.swing.JButton cancelButton;
-    private javax.swing.JComboBox dataTypeComboBox;
-    private javax.swing.JComboBox genomeComboBox;
-    private com.jidesoft.combobox.FileChooserComboBox inputFileControl;
-    private javax.swing.JLabel jLabel1;
-    private javax.swing.JLabel jLabel2;
-    private javax.swing.JLabel jLabel3;
-    private javax.swing.JLabel jLabel4;
-    private javax.swing.JLabel missingDataExplanation;
-    private javax.swing.JButton okButton;
-    private com.jidesoft.combobox.FileChooserComboBox outputFileControl;
-    private javax.swing.JProgressBar progressBar;
-    // End of variables declaration//GEN-END:variables
-}
diff --git a/src/org/broad/igv/ui/ROIEditor.java b/src/org/broad/igv/ui/ROIEditor.java
new file mode 100644
index 0000000..3386b92
--- /dev/null
+++ b/src/org/broad/igv/ui/ROIEditor.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * Created by JFormDesigner on Fri Aug 20 12:06:20 EDT 2010
+ */
+
+package org.broad.igv.ui;
+
+import java.awt.*;
+import javax.swing.*;
+import javax.swing.border.*;
+
+/**
+ * @author Stan Diamond
+ */
+public class ROIEditor extends JDialog {
+    public ROIEditor(Frame owner) {
+        super(owner);
+        initComponents();
+    }
+
+    public ROIEditor(Dialog owner) {
+        super(owner);
+        initComponents();
+    }
+
+    private void initComponents() {
+        // JFormDesigner - Component initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
+        // Generated using JFormDesigner non-commercial license
+        dialogPane = new JPanel();
+        contentPanel = new JPanel();
+        label1 = new JLabel();
+        textField1 = new JTextField();
+        textField2 = new JTextField();
+        textField3 = new JTextField();
+        label2 = new JLabel();
+        label3 = new JLabel();
+        label4 = new JLabel();
+        scrollPane1 = new JScrollPane();
+        textPane1 = new JTextPane();
+        buttonBar = new JPanel();
+        okButton = new JButton();
+
+        //======== this ========
+        Container contentPane = getContentPane();
+        contentPane.setLayout(new BorderLayout());
+
+        //======== dialogPane ========
+        {
+            dialogPane.setBorder(new EmptyBorder(12, 12, 12, 12));
+            dialogPane.setLayout(new BorderLayout());
+
+            //======== contentPanel ========
+            {
+                contentPanel.setLayout(null);
+
+                //---- label1 ----
+                label1.setText("Chromosome");
+                contentPanel.add(label1);
+                label1.setBounds(new Rectangle(new Point(5, 20), label1.getPreferredSize()));
+                contentPanel.add(textField1);
+                textField1.setBounds(105, 20, 185, textField1.getPreferredSize().height);
+                contentPanel.add(textField2);
+                textField2.setBounds(105, 50, 190, textField2.getPreferredSize().height);
+                contentPanel.add(textField3);
+                textField3.setBounds(105, 85, 195, textField3.getPreferredSize().height);
+
+                //---- label2 ----
+                label2.setText("Start");
+                contentPanel.add(label2);
+                label2.setBounds(new Rectangle(new Point(5, 55), label2.getPreferredSize()));
+
+                //---- label3 ----
+                label3.setText("End");
+                contentPanel.add(label3);
+                label3.setBounds(new Rectangle(new Point(5, 90), label3.getPreferredSize()));
+
+                //---- label4 ----
+                label4.setText("Description");
+                contentPanel.add(label4);
+                label4.setBounds(new Rectangle(new Point(5, 130), label4.getPreferredSize()));
+
+                //======== scrollPane1 ========
+                {
+                    scrollPane1.setViewportView(textPane1);
+                }
+                contentPanel.add(scrollPane1);
+                scrollPane1.setBounds(105, 130, 340, 70);
+
+                { // compute preferred size
+                    Dimension preferredSize = new Dimension();
+                    for(int i = 0; i < contentPanel.getComponentCount(); i++) {
+                        Rectangle bounds = contentPanel.getComponent(i).getBounds();
+                        preferredSize.width = Math.max(bounds.x + bounds.width, preferredSize.width);
+                        preferredSize.height = Math.max(bounds.y + bounds.height, preferredSize.height);
+                    }
+                    Insets insets = contentPanel.getInsets();
+                    preferredSize.width += insets.right;
+                    preferredSize.height += insets.bottom;
+                    contentPanel.setMinimumSize(preferredSize);
+                    contentPanel.setPreferredSize(preferredSize);
+                }
+            }
+            dialogPane.add(contentPanel, BorderLayout.NORTH);
+
+            //======== buttonBar ========
+            {
+                buttonBar.setBorder(new EmptyBorder(12, 0, 0, 0));
+                buttonBar.setLayout(new GridBagLayout());
+                ((GridBagLayout)buttonBar.getLayout()).columnWidths = new int[] {0, 80};
+                ((GridBagLayout)buttonBar.getLayout()).columnWeights = new double[] {1.0, 0.0};
+
+                //---- okButton ----
+                okButton.setText("OK");
+                buttonBar.add(okButton, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.BOTH,
+                    new Insets(0, 0, 0, 0), 0, 0));
+            }
+            dialogPane.add(buttonBar, BorderLayout.SOUTH);
+        }
+        contentPane.add(dialogPane, BorderLayout.CENTER);
+        pack();
+        setLocationRelativeTo(getOwner());
+        // JFormDesigner - End of component initialization  //GEN-END:initComponents
+    }
+
+    // JFormDesigner - Variables declaration - DO NOT MODIFY  //GEN-BEGIN:variables
+    // Generated using JFormDesigner non-commercial license
+    private JPanel dialogPane;
+    private JPanel contentPanel;
+    private JLabel label1;
+    private JTextField textField1;
+    private JTextField textField2;
+    private JTextField textField3;
+    private JLabel label2;
+    private JLabel label3;
+    private JLabel label4;
+    private JScrollPane scrollPane1;
+    private JTextPane textPane1;
+    private JPanel buttonBar;
+    private JButton okButton;
+    // JFormDesigner - End of variables declaration  //GEN-END:variables
+}
diff --git a/src/org/broad/igv/ui/ROIEditor.jfd b/src/org/broad/igv/ui/ROIEditor.jfd
new file mode 100644
index 0000000..a9ae084
--- /dev/null
+++ b/src/org/broad/igv/ui/ROIEditor.jfd
@@ -0,0 +1,354 @@
+<?xml version="1.0" encoding="UTF-8"?> 
+<java version="1.6.0_17" class="java.beans.XMLDecoder"> 
+ <object class="com.jformdesigner.model.FormModel"> 
+  <void property="contentType"> 
+   <string>form/swing</string> 
+  </void> 
+  <void property="root"> 
+   <object class="com.jformdesigner.model.FormRoot"> 
+    <void method="add"> 
+     <object class="com.jformdesigner.model.FormWindow"> 
+      <string>javax.swing.JDialog</string> 
+      <object class="com.jformdesigner.model.FormLayoutManager"> 
+       <class>java.awt.BorderLayout</class> 
+      </object> 
+      <void method="add"> 
+       <object class="com.jformdesigner.model.FormContainer"> 
+        <string>javax.swing.JPanel</string> 
+        <object class="com.jformdesigner.model.FormLayoutManager"> 
+         <class>java.awt.BorderLayout</class> 
+        </object> 
+        <void method="setProperty"> 
+         <string>border</string> 
+         <object class="javax.swing.border.EmptyBorder"> 
+          <object class="java.awt.Insets"> 
+           <int>12</int> 
+           <int>12</int> 
+           <int>12</int> 
+           <int>12</int> 
+          </object> 
+         </object> 
+        </void> 
+        <void property="name"> 
+         <string>dialogPane</string> 
+        </void> 
+        <void method="add"> 
+         <object class="com.jformdesigner.model.FormContainer"> 
+          <string>javax.swing.JPanel</string> 
+          <object class="com.jformdesigner.model.FormLayoutManager"> 
+           <class>com.jformdesigner.runtime.NullLayout</class> 
+          </object> 
+          <void property="name"> 
+           <string>contentPanel</string> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormComponent"> 
+            <string>javax.swing.JLabel</string> 
+            <void method="setProperty"> 
+             <string>text</string> 
+             <string>Chromosome</string> 
+            </void> 
+            <void property="name"> 
+             <string>label1</string> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.NullConstraints</class> 
+            <void method="setProperty"> 
+             <string>y</string> 
+             <int>20</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>x</string> 
+             <int>5</int> 
+            </void> 
+           </object> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormComponent"> 
+            <string>javax.swing.JTextField</string> 
+            <void property="name"> 
+             <string>textField1</string> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.NullConstraints</class> 
+            <void method="setProperty"> 
+             <string>x</string> 
+             <int>105</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>y</string> 
+             <int>20</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>width</string> 
+             <int>185</int> 
+            </void> 
+           </object> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormComponent"> 
+            <string>javax.swing.JTextField</string> 
+            <void property="name"> 
+             <string>textField2</string> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.NullConstraints</class> 
+            <void method="setProperty"> 
+             <string>x</string> 
+             <int>105</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>y</string> 
+             <int>50</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>width</string> 
+             <int>190</int> 
+            </void> 
+           </object> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormComponent"> 
+            <string>javax.swing.JTextField</string> 
+            <void property="name"> 
+             <string>textField3</string> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.NullConstraints</class> 
+            <void method="setProperty"> 
+             <string>x</string> 
+             <int>105</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>y</string> 
+             <int>85</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>width</string> 
+             <int>195</int> 
+            </void> 
+           </object> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormComponent"> 
+            <string>javax.swing.JLabel</string> 
+            <void method="setProperty"> 
+             <string>text</string> 
+             <string>Start</string> 
+            </void> 
+            <void property="name"> 
+             <string>label2</string> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.NullConstraints</class> 
+            <void method="setProperty"> 
+             <string>x</string> 
+             <int>5</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>y</string> 
+             <int>55</int> 
+            </void> 
+           </object> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormComponent"> 
+            <string>javax.swing.JLabel</string> 
+            <void method="setProperty"> 
+             <string>text</string> 
+             <string>End</string> 
+            </void> 
+            <void property="name"> 
+             <string>label3</string> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.NullConstraints</class> 
+            <void method="setProperty"> 
+             <string>x</string> 
+             <int>5</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>y</string> 
+             <int>90</int> 
+            </void> 
+           </object> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormComponent"> 
+            <string>javax.swing.JLabel</string> 
+            <void method="setProperty"> 
+             <string>text</string> 
+             <string>Description</string> 
+            </void> 
+            <void property="name"> 
+             <string>label4</string> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.NullConstraints</class> 
+            <void method="setProperty"> 
+             <string>x</string> 
+             <int>5</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>y</string> 
+             <int>130</int> 
+            </void> 
+           </object> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormContainer"> 
+            <string>javax.swing.JScrollPane</string> 
+            <object class="com.jformdesigner.model.FormLayoutManager"> 
+             <class>javax.swing.JScrollPane</class> 
+            </object> 
+            <void property="name"> 
+             <string>scrollPane1</string> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormComponent"> 
+              <string>javax.swing.JTextPane</string> 
+              <void property="name"> 
+               <string>textPane1</string> 
+              </void> 
+             </object> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.NullConstraints</class> 
+            <void method="setProperty"> 
+             <string>x</string> 
+             <int>105</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>y</string> 
+             <int>130</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>width</string> 
+             <int>340</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>height</string> 
+             <int>70</int> 
+            </void> 
+           </object> 
+          </void> 
+         </object> 
+         <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+          <class>java.lang.String</class> 
+          <void method="setProperty"> 
+           <string>value</string> 
+           <string>North</string> 
+          </void> 
+         </object> 
+        </void> 
+        <void method="add"> 
+         <object class="com.jformdesigner.model.FormContainer"> 
+          <string>javax.swing.JPanel</string> 
+          <object class="com.jformdesigner.model.FormLayoutManager"> 
+           <class>java.awt.GridBagLayout</class> 
+           <void method="setProperty"> 
+            <string>$columnSpecs</string> 
+            <string>0:1.0, 80</string> 
+           </void> 
+           <void method="setProperty"> 
+            <string>$rowSpecs</string> 
+            <string>0</string> 
+           </void> 
+           <void method="setProperty"> 
+            <string>$hGap</string> 
+            <int>5</int> 
+           </void> 
+           <void method="setProperty"> 
+            <string>$vGap</string> 
+            <int>5</int> 
+           </void> 
+          </object> 
+          <void method="setProperty"> 
+           <string>border</string> 
+           <object class="javax.swing.border.EmptyBorder"> 
+            <object class="java.awt.Insets"> 
+             <int>12</int> 
+             <int>0</int> 
+             <int>0</int> 
+             <int>0</int> 
+            </object> 
+           </object> 
+          </void> 
+          <void property="name"> 
+           <string>buttonBar</string> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormComponent"> 
+            <string>javax.swing.JButton</string> 
+            <void method="setProperty"> 
+             <string>text</string> 
+             <string>OK</string> 
+            </void> 
+            <void property="name"> 
+             <string>okButton</string> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.GridBagConstraintsEx</class> 
+            <void method="setProperty"> 
+             <string>gridx</string> 
+             <int>1</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>gridy</string> 
+             <int>0</int> 
+            </void> 
+           </object> 
+          </void> 
+         </object> 
+         <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+          <class>java.lang.String</class> 
+          <void method="setProperty"> 
+           <string>value</string> 
+           <string>South</string> 
+          </void> 
+         </object> 
+        </void> 
+       </object> 
+       <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+        <class>java.lang.String</class> 
+        <void method="setProperty"> 
+         <string>value</string> 
+         <string>Center</string> 
+        </void> 
+       </object> 
+      </void> 
+      <void property="name"> 
+       <string>this</string> 
+      </void> 
+     </object> 
+     <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+      <null/> 
+      <void method="setProperty"> 
+       <string>location</string> 
+       <object class="java.awt.Point"> 
+        <int>0</int> 
+        <int>0</int> 
+       </object> 
+      </void> 
+      <void method="setProperty"> 
+       <string>size</string> 
+       <object class="java.awt.Dimension"> 
+        <int>510</int> 
+        <int>425</int> 
+       </object> 
+      </void> 
+     </object> 
+    </void> 
+   </object> 
+  </void> 
+ </object> 
+</java> 
diff --git a/src/org/broad/igv/ui/RegionOfInterest.java b/src/org/broad/igv/ui/RegionOfInterest.java
index a2be93d..b2c2867 100644
--- a/src/org/broad/igv/ui/RegionOfInterest.java
+++ b/src/org/broad/igv/ui/RegionOfInterest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -19,38 +19,38 @@
 
 package org.broad.igv.ui;
 
-//~--- non-JDK imports --------------------------------------------------------
 
-import org.broad.igv.PreferenceManager;
-import org.broad.igv.roc.ROC;
-import org.broad.igv.roc.ROCScore;
-import org.broad.igv.roc.ROCUtils;
+
+import org.broad.igv.ui.util.MessageUtils;
+import org.broad.igv.feature.SequenceManager;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.track.RegionScoreType;
-import org.broad.igv.track.TrackManager;
+import org.broad.igv.track.TrackType;
 
 import javax.swing.*;
 import javax.swing.event.MouseInputAdapter;
 import java.awt.*;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.MouseEvent;
 import java.util.Collection;
 import java.util.Iterator;
-import java.util.List;
+import java.util.Set;
 
 /**
  * @author eflakes
  */
-public class RegionOfInterest extends MouseInputAdapter {
+public class RegionOfInterest{
 
-    private String chromosomeName;
+    private String chr;
     private String description;
     private Integer start;    // In Chromosome coordinates
     private Integer end;      // In Chromosome coordinates
     private Color backgroundColor = Color.RED;
     private Color foregroundColor = Color.BLACK;
-    private JPopupMenu popupMenu;
-    private Component parent;
+
     private WaitCursorManager.CursorToken token;
 
     /**
@@ -63,315 +63,55 @@ public class RegionOfInterest extends MouseInputAdapter {
      */
     public RegionOfInterest(String chromosomeName, Integer start, Integer end, String description) {
 
-        this.chromosomeName = chromosomeName;
+        this.chr = chromosomeName;
         this.description = description;
         this.start = start;
         this.end = end;
     }
 
+    public String getTooltip() {
+        return description == null ? chr + ":" + start + "-" + end : description;
+    }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public String getChromosomeName() {
-        return chromosomeName;
+    public String getChr() {
+        return chr;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public String getDescription() {
         return description;
     }
 
-    /**
-     * Method description
-     *
-     * @param end
-     */
+
     public void setEnd(Integer end) {
         this.end = end;
     }
 
-    /**
-     * Method description
-     *
-     * @param start
-     */
     public void setStart(Integer start) {
         this.start = start;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public Integer getEnd() {
         return end;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public Integer getStart() {
         return start;
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
     public Color getBackgroundColor() {
         return backgroundColor;
     }
 
-    /**
-     * Method description
-     *
-     * @param backgroundColor
-     */
-    public void setBackgroundColor(Color backgroundColor) {
-        this.backgroundColor = backgroundColor;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
     public Color getForegroundColor() {
         return foregroundColor;
     }
 
-    /**
-     * Method description
-     *
-     * @param foregroundColor
-     */
-    public void setForegroundColor(Color foregroundColor) {
-        this.foregroundColor = foregroundColor;
-    }
-
-    protected JPopupMenu getPopupMenu(final Point point) {
-
-        popupMenu = new JPopupMenu();
-
-        // Create and add a menu item
-
-        JMenuItem item = new JMenuItem("Sort by amplification");
-        item.addActionListener(new ActionListener() {
-
-            public void actionPerformed(ActionEvent e) {
-
-                TrackManager.getInstance().sortGroup(RegionOfInterest.this,
-                        RegionScoreType.AMPLIFICATION);
-                IGVMainFrame.getInstance().clearImageCacheWithNoRepaint();
-                IGVMainFrame.getInstance().getContentPane().repaint();
-            }
-        });
-        popupMenu.add(item);
-
-
-        item = new JMenuItem("Sort by deletion");
-        item.addActionListener(new ActionListener() {
-
-            public void actionPerformed(ActionEvent e) {
-
-                TrackManager.getInstance().sortGroup(RegionOfInterest.this,
-                        RegionScoreType.DELETION);
-                IGVMainFrame.getInstance().clearImageCacheWithNoRepaint();
-                IGVMainFrame.getInstance().getContentPane().repaint();
-            }
-        });
-        popupMenu.add(item);
-
-        item = new JMenuItem("Sort by expression");
-        item.addActionListener(new ActionListener() {
-
-            public void actionPerformed(ActionEvent e) {
-
-                TrackManager.getInstance().sortGroup(RegionOfInterest.this,
-                        RegionScoreType.EXPRESSION);
-                IGVMainFrame.getInstance().clearImageCacheWithNoRepaint();
-                IGVMainFrame.getInstance().getContentPane().repaint();
-            }
-        });
-
-        popupMenu.add(item);
-
-        item = new JMenuItem("Sort by value");
-        item.addActionListener(new ActionListener() {
-
-            public void actionPerformed(ActionEvent e) {
-
-                TrackManager.getInstance().sortGroup(RegionOfInterest.this, RegionScoreType.SCORE);
-                IGVMainFrame.getInstance().fireViewChangedEvent();
-                IGVMainFrame.getInstance().getContentPane().repaint();
-            }
-        });
-
-        popupMenu.add(item);
-
-        if (ROC.ENABLED) {
-            item = new JMenuItem("Search for corrleated genes (ROC)");
-            item.addActionListener(new ActionListener() {
-
-                public void actionPerformed(ActionEvent e) {
-                    String att = PreferenceManager.getInstance().getOverlayAttribute();
-                    ViewContext vc = IGVModel.getInstance().getViewContext();
-                    ROCUtils rocUtils = new ROCUtils(att, chromosomeName, start, end, vc.getZoom());
 
-                    float threshold = 0.1f;
-                    List<ROCScore> scores = rocUtils.computeScores(threshold);
-                    for (ROCScore score : scores) {
-                        System.out.println(score.getGene() + "\t" + score.getAUC());
-                    }
-                }
-            });
-
-            popupMenu.add(item);
-
-        }
-
-        popupMenu.add(new JSeparator());
-
-        item = new JMenuItem("Delete");
-        item.addActionListener(new ActionListener() {
-
-            public void actionPerformed(ActionEvent e) {
-
-                ViewContext viewContext = IGVModel.getInstance().getViewContext();
-                Collection<RegionOfInterest> regionsOfInterest =
-                        IGVModel.getInstance().getRegionsOfInterest(chromosomeName);
-
-                if (regionsOfInterest != null) {
-
-                    Iterator iterator = regionsOfInterest.iterator();
-                    while (iterator.hasNext()) {
-                        RegionOfInterest region = (RegionOfInterest) iterator.next();
-
-                        Integer start = region.getStart();
-                        Integer end = region.getEnd();
-
-                        if ((start == null) || (end == null)) {
-                            continue;
-                        }
-
-                        Integer regionStart = viewContext.getPixelPosition(start);
-                        Integer regionEnd = viewContext.getPixelPosition(end);
-
-                        if ((regionStart == null) || (regionEnd == null)) {
-                            continue;
-                        }
-
-                        if ((point.x >= regionStart) && (point.x <= regionEnd)) {
-                            iterator.remove();
-                        }
-                    }
-                }
-                IGVMainFrame.getInstance().repaintDataAndHeaderPanels();
-            }
-        });
-        popupMenu.add(item);
-
-        return popupMenu;
-    }
-
-    /**
-     * Method description
-     *
-     * @param e
-     */
-    @Override
-    public void mousePressed(MouseEvent e) {
-        showPopup(e);
-    }
-
-    /**
-     * Method description
-     *
-     * @param e
-     */
-    @Override
-    public void mouseReleased(MouseEvent e) {
-        showPopup(e);
-    }
-
-    /**
-     * Method description
-     *
-     * @param e
-     */
-    @Override
-    public void mouseEntered(MouseEvent e) {
-    }
-
-    /**
-     * Method description
-     *
-     * @param e
-     */
-    @Override
-    public void mouseExited(MouseEvent e) {
-    }
-
-    /**
-     * Method description
-     *
-     * @param e
-     */
-    @Override
-    public void mouseMoved(MouseEvent e) {
-
-        Point point = e.getPoint();
-
-        ViewContext viewContext = IGVModel.getInstance().getViewContext();
-        if ((point.x >= viewContext.getPixelPosition(start))
-                && (point.x <= viewContext.getPixelPosition(end))) {
-
-            parent.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
-
-        } else {
-            parent.setCursor(Cursor.getDefaultCursor());
-        }
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public Component getParent() {
-        return parent;
-    }
-
-    private void showPopup(MouseEvent e) {
-
-        ViewContext viewContext = IGVModel.getInstance().getViewContext();
-        Point point = e.getPoint();
-
-        if ((point.x >= viewContext.getPixelPosition(start))
-                && (point.x <= viewContext.getPixelPosition(end))) {
-
-            getPopupMenu(point).show(e.getComponent(), e.getX(), e.getY());
-
-        }
-    }
-
-    /**
-     * Method description
-     *
-     * @param parent
-     */
-    public void setParent(Component parent) {
-        this.parent = parent;
+    public String getLocusString() {
+        return getChr() + ":" + getStart() + "-" + getEnd();
     }
 }
diff --git a/src/org/broad/igv/ui/RegionOfInterestTool.java b/src/org/broad/igv/ui/RegionOfInterestTool.java
index 1a8e962..ac31ce8 100644
--- a/src/org/broad/igv/ui/RegionOfInterestTool.java
+++ b/src/org/broad/igv/ui/RegionOfInterestTool.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,6 +22,8 @@
  */
 package org.broad.igv.ui;
 
+import org.broad.igv.session.ViewContext;
+
 import javax.swing.*;
 import java.awt.*;
 import java.awt.event.MouseEvent;
@@ -51,7 +53,7 @@ public class RegionOfInterestTool extends IGVTool {
     @Override
     public void mousePressed(final MouseEvent e) {
 
-        ViewContext viewContext = IGVModel.getInstance().getViewContext();
+        ViewContext viewContext = ViewContext.getInstance();
 
         if (e.getButton() == MouseEvent.BUTTON1 &&
                 e.getClickCount() == 1) {
@@ -89,7 +91,7 @@ public class RegionOfInterestTool extends IGVTool {
                                             chromosomeName,
                                             start,
                                             end,
-                                            "ROI");
+                                            null);
                             // TODO -- get this ugly reference out of here
                             IGVMainFrame.getInstance().endROI();
                             IGVMainFrame.getInstance().addRegionOfInterest(regionOfInterest);
diff --git a/src/org/broad/igv/ui/ResourceCheckBoxList.java b/src/org/broad/igv/ui/ResourceCheckBoxList.java
deleted file mode 100644
index 91c1723..0000000
--- a/src/org/broad/igv/ui/ResourceCheckBoxList.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-package org.broad.igv.ui;
-
-import javax.swing.*;
-import java.awt.*;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.net.URL;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * @author eflakes
- */
-public class ResourceCheckBoxList extends JList {
-
-    /**
-     * A Checkbox representation
-     */
-    public static class ResourceCheckBox extends JCheckBox {
-
-        private URL resourceUrl;
-
-        public ResourceCheckBox(String displayName, URL url) {
-            super(displayName);
-            resourceUrl = url;
-        }
-
-        public URL getResourceLocator() {
-            return resourceUrl;
-        }
-    }
-
-    public ResourceCheckBoxList(Object[] data) {
-        super(data);
-        setCellRenderer(new JListCellRenderer());
-        setEnabled(true);
-        addMouseListener(new MouseAdapter() {
-
-            @Override
-            public void mousePressed(MouseEvent e) {
-                int index = locationToIndex(e.getPoint());
-                if (index != -1) {
-                    ResourceCheckBox resourceCheckBox =
-                            (ResourceCheckBox) getModel().getElementAt(index);
-                    resourceCheckBox.setSelected(!resourceCheckBox.isSelected());
-                    repaint();
-                }
-            }
-        });
-    }
-
-    /**
-     * Renderer
-     */
-    class JListCellRenderer implements ListCellRenderer {
-
-        public Component getListCellRendererComponent(JList list, Object value,
-                                                      int index, boolean isSelected, boolean cellHasFocus) {
-            ResourceCheckBox resourceCheckBox = (ResourceCheckBox) value;
-            if (isSelected) {
-                resourceCheckBox.setBackground(list.getSelectionBackground());
-                resourceCheckBox.setForeground(list.getSelectionForeground());
-            } else {
-                resourceCheckBox.setBackground(list.getBackground());
-                resourceCheckBox.setForeground(list.getForeground());
-            }
-
-            resourceCheckBox.setEnabled(list.isEnabled());
-            resourceCheckBox.setFont(list.getFont());
-            resourceCheckBox.setOpaque(true);
-            return resourceCheckBox;
-        }
-    }
-
-    public Set<URL> getSelectedURLs() {
-
-        HashSet<URL> urls = new HashSet();
-        ListModel model = ResourceCheckBoxList.this.getModel();
-        int modelSize = model.getSize();
-        for (int i = 0; i < modelSize; i++) {
-
-            ResourceCheckBoxList.ResourceCheckBox resourceCheckBox =
-                    ((ResourceCheckBoxList.ResourceCheckBox) model.getElementAt(i));
-            if (resourceCheckBox.isSelected()) {
-                urls.add(resourceCheckBox.getResourceLocator());
-            }
-        }
-        return urls;
-    }
-}
diff --git a/src/org/broad/igv/ui/ResourceFileBuilder.java b/src/org/broad/igv/ui/ResourceFileBuilder.java
index a2995c3..5301077 100644
--- a/src/org/broad/igv/ui/ResourceFileBuilder.java
+++ b/src/org/broad/igv/ui/ResourceFileBuilder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/ResourceFileBuilderDialog.java b/src/org/broad/igv/ui/ResourceFileBuilderDialog.java
index 9bbfea3..0ddeef4 100644
--- a/src/org/broad/igv/ui/ResourceFileBuilderDialog.java
+++ b/src/org/broad/igv/ui/ResourceFileBuilderDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/ResourceTree.java b/src/org/broad/igv/ui/ResourceTree.java
index 864b6c2..6829152 100644
--- a/src/org/broad/igv/ui/ResourceTree.java
+++ b/src/org/broad/igv/ui/ResourceTree.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -21,11 +21,12 @@ package org.broad.igv.ui;
 import org.apache.log4j.Logger;
 import org.broad.igv.PreferenceManager;
 import org.broad.igv.track.AttributeManager;
-import org.broad.igv.track.TrackManager;
 import org.broad.igv.ui.util.LinkCheckBox;
+import org.broad.igv.util.ColorUtilities;
 import org.broad.igv.util.ResourceLocator;
+
 import static org.broad.igv.util.ResourceLocator.AttributeType.*;
-import org.broad.igv.util.Utilities;
+
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
@@ -39,7 +40,6 @@ import java.awt.event.ItemListener;
 import java.awt.event.MouseEvent;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
-import java.io.InputStream;
 import java.util.*;
 import java.util.List;
 
@@ -49,8 +49,7 @@ import java.util.List;
 public class ResourceTree {
 
     private StringBuffer buffer = new StringBuffer();
-    private static String FAILED_TO_CREATE_RESOURCE_TREE_DIALOG =
-            "Failure while creating the resource tree dialog";
+    private static String FAILED_TO_CREATE_RESOURCE_TREE_DIALOG = "Failure while creating the resource tree dialog";
     private static Logger log = Logger.getLogger(ResourceTree.class);
     private static String XML_ROOT = "Global";
     private List<CheckableResource> leafResources = new ArrayList();
@@ -74,30 +73,24 @@ public class ResourceTree {
         return theInstance;
     }
 
-    /**
-     * Shows a tree of selectable resources.
-     *
-     * @param inStream    The data that represents an XML resouece tree.
-     * @param dialogTitle
-     * @return the resources selected by user.
-     */
-    public HashSet<ResourceLocator>
-    showResourceTreeDialog(Component parent, InputStream inStream,
-                           String dialogTitle) {
 
-        HashSet<ResourceLocator> locators = null;
-        try {
-            Document document =
-                    Utilities.createDOMDocumentFromXmlStream(inStream);
-            locators =
-                    showResourceTreeDialog(parent, document, dialogTitle);
-        } catch (Exception e) {
-            log.error(FAILED_TO_CREATE_RESOURCE_TREE_DIALOG, e);
+    static class CancelableOptionPane extends JOptionPane {
+        private boolean canceled = false;
+
+        CancelableOptionPane(Object o, int i, int i1) {
+            super(o, i, i1);
+        }
+
+        public boolean isCanceled() {
+            return canceled;
         }
 
-        return locators;
+        public void setCanceled(boolean canceled) {
+            this.canceled = canceled;
+        }
     }
 
+
     /**
      * Shows a tree of selectable resources.
      *
@@ -105,11 +98,10 @@ public class ResourceTree {
      * @param dialogTitle
      * @return the resources selected by user.
      */
-    public HashSet<ResourceLocator>
-    showResourceTreeDialog(Component parent, Document document,
-                           String dialogTitle) {
+    public HashSet<ResourceLocator> showResourceTreeDialog(Component parent, Document document, String dialogTitle) {
 
         JDialog dialog = null;
+
         final HashSet<ResourceLocator> locators = new HashSet();
         try {
 
@@ -118,9 +110,8 @@ public class ResourceTree {
 
             int optionType = JOptionPane.OK_CANCEL_OPTION;
             int messageType = JOptionPane.PLAIN_MESSAGE;
-            final JOptionPane optionPane =
-                    new JOptionPane(
-                            new JScrollPane(dialogTree), messageType, optionType);
+            final CancelableOptionPane optionPane =
+                    new CancelableOptionPane(new JScrollPane(dialogTree), messageType, optionType);
 
             optionPane.setPreferredSize(new Dimension(650, 500));
             optionPane.setOpaque(true);
@@ -132,10 +123,11 @@ public class ResourceTree {
 
                             Object value = e.getNewValue();
                             if (value instanceof Integer) {
-
                                 int option = (Integer) value;
+                                if (option == JOptionPane.CANCEL_OPTION) {
+                                    optionPane.setCanceled(true);
 
-                                if (option == JOptionPane.OK_OPTION) {
+                                } else {
 
                                     TreeModel model = dialogTree.getModel();
 
@@ -163,15 +155,13 @@ public class ResourceTree {
                                             buffer.append("\t");
                                         }
                                     }
-                                    PreferenceManager.getInstance().
-                                            setCheckedResources(buffer.toString());
+                                    PreferenceManager.getInstance().setCheckedResources(buffer.toString());
                                 }
                             }
                         }
                     });
 
-            dialog = optionPane.createDialog(parent,
-                    (dialogTitle == null ? "Resource Tree" : dialogTitle));
+            dialog = optionPane.createDialog(parent, (dialogTitle == null ? "Resource Tree" : dialogTitle));
             dialog.setBackground(Color.WHITE);
             dialog.getContentPane().setBackground(Color.WHITE);
 
@@ -186,11 +176,14 @@ public class ResourceTree {
             dialog.pack();
             dialog.setLocationRelativeTo(IGVMainFrame.getInstance());
             dialog.setVisible(true);
+
+            return  optionPane.isCanceled() ? null :  locators;
         } catch (Exception e) {
             log.error(FAILED_TO_CREATE_RESOURCE_TREE_DIALOG, e);
+            return null;
         }
 
-        return locators;
+
     }
 
     private JTree createTreeFromDOM(Document document) {
@@ -218,7 +211,7 @@ public class ResourceTree {
         tree.setCellEditor(new ResourceEditor(tree));
         tree.setEditable(true);
 
-        Set<ResourceLocator> loadedResources = TrackManager.getInstance().getDataResourceLocators();
+        Set<ResourceLocator> loadedResources = IGVMainFrame.getInstance().getTrackManager().getDataResourceLocators();
         loadedResources.addAll(AttributeManager.getInstance().getLoadedResources());
 
         // Build and attach descentants of the root node to the tree
@@ -246,29 +239,44 @@ public class ResourceTree {
     private void processNode(DefaultMutableTreeNode treeNode, Node xmlNode,
                              Set<ResourceLocator> loadedResources) {
 
-        String resourceLabel = getAttribute((Element) xmlNode, NAME.getText());
+        String name = getAttribute((Element) xmlNode, NAME.getText());
 
         ResourceLocator locator = new ResourceLocator(
                 getAttribute((Element) xmlNode, SERVER_URL.getText()),
                 getAttribute((Element) xmlNode, PATH.getText()));
 
-        String resourceType =  getAttribute((Element) xmlNode, RESOURCE_TYPE.getText());
-        if(resourceType != null) {
+        String resourceType = getAttribute((Element) xmlNode, RESOURCE_TYPE.getText());
+        if (resourceType != null) {
             locator.setType(resourceType);
         }
 
-        locator.setHyperLink(getAttribute((Element) xmlNode, HYPERLINK.getText()));
+        locator.setInfolink(getAttribute((Element) xmlNode, HYPERLINK.getText()));
+        locator.setSampleId(getAttribute((Element) xmlNode, SAMPLE_ID.getText()));
+        locator.setUrl(getAttribute((Element) xmlNode, URL.getText()));
         locator.setDescription(getAttribute((Element) xmlNode, DESCRIPTION.getText()));
-        locator.setName(resourceLabel);
+        locator.setName(name);
 
         // Special element for alignment tracks
         String coverage = getAttribute((Element) xmlNode, "coverage");
         if (coverage != null) {
             locator.setCoverage(coverage);
         }
+        locator.setTrackLine(getAttribute((Element) xmlNode, TRACK_LINE.getText()));
 
-        CheckableResource resource =
-                new CheckableResource(resourceLabel, false, locator);
+        if (coverage != null) {
+            locator.setCoverage(coverage);
+        }
+        String colorString = getAttribute((Element) xmlNode, "color");
+        if (colorString != null) {
+            try {
+                Color c = ColorUtilities.getColorFromString(colorString);
+                locator.setColor(c);
+            } catch (Exception e) {
+                log.error("Error setting color: ", e);
+            }
+        }
+
+        CheckableResource resource = new CheckableResource(name, false, locator);
         resource.setEnabled(tree.isEnabled());
         treeNode.setUserObject(resource);
 
@@ -287,13 +295,10 @@ public class ResourceTree {
             // Need to check class of child node, its not neccessarily an
             // element (could be a comment for example).
             if (xmlChildNode instanceof Element) {
-                String categoryLabel =
-                        getAttribute((Element) xmlChildNode, NAME.getText());
-                DefaultMutableTreeNode treeChildNode =
-                        new DefaultMutableTreeNode(categoryLabel);
+                String categoryLabel = getAttribute((Element) xmlChildNode, NAME.getText());
+                DefaultMutableTreeNode treeChildNode = new DefaultMutableTreeNode(categoryLabel);
                 treeNode.add(treeChildNode);
-                processNode(
-                        treeChildNode, (Element) xmlChildNode, loadedResources);
+                processNode(treeChildNode, (Element) xmlChildNode, loadedResources);
             }
         }
 
@@ -318,8 +323,7 @@ public class ResourceTree {
             resource.setSelected(hasSelectedChildren);
 
             if (true || hasSelectedChildren) {
-                ResourceEditor.checkOrUncheckParentNodesRecursively(
-                        treeNode, true);
+                ResourceEditor.checkOrUncheckParentNodesRecursively(treeNode, true);
             }
         }
 
@@ -407,8 +411,10 @@ public class ResourceTree {
     static class NodeRenderer implements TreeCellRenderer {
 
         private LinkCheckBox renderer = new LinkCheckBox();
-        private Color selectionForeground, selectionBackground;
-        private Color textForeground, textBackground;
+        private Color selectionForeground;
+        private Color selectionBackground;
+        private Color textForeground;
+        private Color textBackground;
 
         public NodeRenderer() {
 
@@ -422,14 +428,10 @@ public class ResourceTree {
             renderer.setFocusPainted(
                     (booleanValue != null) && (booleanValue.booleanValue()));
 
-            selectionForeground =
-                    UIManager.getColor("Tree.selectionForeground");
-            selectionBackground =
-                    UIManager.getColor("Tree.selectionBackground");
-            textForeground =
-                    UIManager.getColor("Tree.textForeground");
-            textBackground =
-                    UIManager.getColor("Tree.textBackground");
+            selectionForeground = UIManager.getColor("Tree.selectionForeground");
+            selectionBackground = UIManager.getColor("Tree.selectionBackground");
+            textForeground = UIManager.getColor("Tree.textForeground");
+            textBackground = UIManager.getColor("Tree.textBackground");
             renderer.setSelected(false);
         }
 
@@ -474,8 +476,7 @@ public class ResourceTree {
                         renderer.setSelected(resource.isSelected());
                         renderer.setEnabled(resource.isEnabled());
 
-                        String hyperLink =
-                                resource.getResourceLocator().getHyperLink();
+                        String hyperLink = resource.getResourceLocator().getInfolink();
                         if (hyperLink == null) {
                             renderer.showHyperLink(false);
                         } else {
@@ -588,6 +589,7 @@ public class ResourceTree {
         /*
         * Uncheck a node unless rule prevent this behavior.
         */
+
         private void uncheckCurrentNodeIfAllowed(CheckableResource resource,
                                                  TreeNode treeNode) {
 
@@ -1276,7 +1278,7 @@ public class ResourceTree {
 
     static private Set<ResourceLocator> getLoadedResources() {
         Set<ResourceLocator> loadedResources =
-                TrackManager.getInstance().getDataResourceLocators();
+                IGVMainFrame.getInstance().getTrackManager().getDataResourceLocators();
         loadedResources.addAll(AttributeManager.getInstance().getLoadedResources());
         return loadedResources;
     }
diff --git a/src/org/broad/igv/ui/ShutdownThread.java b/src/org/broad/igv/ui/ShutdownThread.java
index 27318d2..fa8c6e4 100644
--- a/src/org/broad/igv/ui/ShutdownThread.java
+++ b/src/org/broad/igv/ui/ShutdownThread.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -17,12 +17,8 @@
  */
 package org.broad.igv.ui;
 
-//~--- non-JDK imports --------------------------------------------------------
 
-import ncsa.hdf.hdf5lib.H5;
-import ncsa.hdf.hdf5lib.exceptions.HDF5LibraryException;
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
 import org.broad.igv.h5.HDF5RemoteReader;
 
 import java.io.File;
@@ -42,28 +38,25 @@ public class ShutdownThread extends Thread {
      */
     @Override
     public void run() {
-        try {
-
-            // Release local and remote HDF5 resources
-            log.info("Closing HDF5");
-            H5.H5close();
-            HDF5RemoteReader.shutdown();
-
-            // Cleanup jnlp files
-            if (IGVConstants.IS_MAC) {
-                File desktop = new File(System.getProperty("user.home") + "/Desktop");
-                if (desktop.exists() && desktop.isDirectory()) {
-                    cleanupJnlpFiles(desktop);
-                }
-                File downloads = new File(System.getProperty("user.home") + "/Downloads");
-                if (downloads.exists() && downloads.isDirectory()) {
-                    cleanupJnlpFiles(downloads);
-                }
 
+        // Release local and remote HDF5 resources
+        //log.info("Closing HDF5");
+        //H5.H5close();
+        HDF5RemoteReader.shutdown();
+
+        // Cleanup jnlp files
+        if (UIConstants.IS_MAC) {
+            File desktop = new File(System.getProperty("user.home") + "/Desktop");
+            if (desktop.exists() && desktop.isDirectory()) {
+                cleanupJnlpFiles(desktop);
             }
-        } catch (HDF5LibraryException ex) {
-            ex.printStackTrace();
+            File downloads = new File(System.getProperty("user.home") + "/Downloads");
+            if (downloads.exists() && downloads.isDirectory()) {
+                cleanupJnlpFiles(downloads);
+            }
+
         }
+
     }
 
     /**
@@ -71,7 +64,6 @@ public class ShutdownThread extends Thread {
      */
     public static void cleanupJnlpFiles(File desktop) {
 
-        File downloads = new File(System.getProperty("user.home") + "/Downloads");
         if (desktop.exists() && desktop.isDirectory()) {
             File[] jnlpFiles = desktop.listFiles(new FileFilter() {
 
diff --git a/src/org/broad/igv/ui/TrackConfigurationDialog.java b/src/org/broad/igv/ui/TrackConfigurationDialog.java
index 1cf030c..1f3d251 100644
--- a/src/org/broad/igv/ui/TrackConfigurationDialog.java
+++ b/src/org/broad/igv/ui/TrackConfigurationDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/TrackFilter.java b/src/org/broad/igv/ui/TrackFilter.java
index 2d63338..46c4d19 100644
--- a/src/org/broad/igv/ui/TrackFilter.java
+++ b/src/org/broad/igv/ui/TrackFilter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -93,11 +93,5 @@ public class TrackFilter extends Filter {
         }
     }
 
-    public boolean isShowAll() {
-        return showAll;
-    }
 
-    public void setShowAll(boolean showAll) {
-        this.showAll = showAll;
-    }
 }
diff --git a/src/org/broad/igv/ui/TrackFilterComponent.java b/src/org/broad/igv/ui/TrackFilterComponent.java
index 0cf0711..538932d 100644
--- a/src/org/broad/igv/ui/TrackFilterComponent.java
+++ b/src/org/broad/igv/ui/TrackFilterComponent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/TrackFilterElement.java b/src/org/broad/igv/ui/TrackFilterElement.java
index d5ea0f4..074ab15 100644
--- a/src/org/broad/igv/ui/TrackFilterElement.java
+++ b/src/org/broad/igv/ui/TrackFilterElement.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -42,7 +42,6 @@ public class TrackFilterElement extends FilterElement {
     public boolean evaluate(Track track, Boolean previousResult) {
 
         String attributeKey = getSelectedItem();
-        AttributeManager manager = AttributeManager.getInstance();
         String attribute = track.getAttributeValue(attributeKey);
 
         return super.test(attribute, previousResult);
diff --git a/src/org/broad/igv/ui/TrackFilterPane.java b/src/org/broad/igv/ui/TrackFilterPane.java
index 1dbf047..70abc23 100644
--- a/src/org/broad/igv/ui/TrackFilterPane.java
+++ b/src/org/broad/igv/ui/TrackFilterPane.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -54,15 +54,17 @@ public class TrackFilterPane extends FilterPane {
     }
 
     public void clearTracks() {
-        ((TrackFilter) getFilter()).clearTracks();
+        getFilter().clearTracks();
     }
 
     public void addTracks(List<Track> tracks) {
-        ((TrackFilter) getFilter()).addTracks(tracks);
+        getFilter().addTracks(tracks);
     }
 
     public FilterComponent createFilterComponent(FilterPane filterPane,
-                                                 String itemListLabel, List<String> itemList, FilterElement element) {
+                                                 String itemListLabel,
+                                                 List<String> itemList,
+                                                 FilterElement element) {
 
         return new TrackFilterComponent((TrackFilterPane) filterPane, itemListLabel, itemList, element);
     }
diff --git a/src/org/broad/igv/ui/UIConstants.java b/src/org/broad/igv/ui/UIConstants.java
index 127d36e..3bf3b0b 100644
--- a/src/org/broad/igv/ui/UIConstants.java
+++ b/src/org/broad/igv/ui/UIConstants.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -15,63 +15,62 @@
  * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
  * FOREGOING.
  */
-
 /*
  * To change this template, choose Tools | Templates
  * and open the template in the editor.
  */
-
 package org.broad.igv.ui;
 
+//~--- JDK imports ------------------------------------------------------------
+
+import org.apache.log4j.Logger;
+import org.broad.igv.Globals;
+import org.broad.igv.exceptions.DataLoadException;
+import org.broad.igv.track.TrackType;
+
+import javax.swing.*;
+import javax.swing.filechooser.FileSystemView;
+import java.awt.*;
+import java.io.File;
+import java.io.IOException;
+import java.util.prefs.Preferences;
+
 /**
- * @author eflakes
+ * @author jrobinso
  */
 public class UIConstants {
 
+    private static Logger log = Logger.getLogger(UIConstants.class);
+    /**
+     * Field description
+     */
+    final public static int groupGap = 10;
+    final public static String APPLICATION_NAME = "IGV";
+    final public static String APPLICATION_LONG_NAME = "Integrative Genomics Viewer";
+    final public static boolean IS_WINDOWS =
+            System.getProperty("os.name").toLowerCase().startsWith("windows");
+    final public static boolean IS_MAC =
+            System.getProperty("os.name").toLowerCase().startsWith("mac");
+    final public static boolean IS_LINUX =
+            System.getProperty("os.name").toLowerCase().startsWith("linux");
+    final public static Dimension preferredSize = new Dimension(1000, 750);
+
+    // To support mutation track overlay.  Generalize later.  Cancer specific option.
+    public static TrackType overlayTrackType = TrackType.MUTATION;
+
+    private static int doubleClickInterval = -1;
     // General
-    final static public String CLICK_ITEM_TO_EDIT_TOOLTIP =
-            "Click this item bring up its editor";
-
-    // Toolbar Menu Item Tooltips
-    final static public String JUMP_TO_WHOLE_GENOME_VIEW_TOOLTIP =
-            "Jump to whole genome view";
-    final static public String JUMP_TO_LOCUS_TOOLTIP =
-            "Jump to Gene or Locus";
-    final static public String SELECT_CHROMOSOME_TOOLTIP =
-            "Select a chromosome to view";
-    final static public String ZOOM_TOOL_TOOLTIP =
-            "Click + to zoom in - Click - to zoom out";
-
-
-    // File Menu Item Tooltips
-    final static public String LOAD_TRACKS_TOOLTIP =
-            "Load data, features or sample information";
-    final static public String LOAD_SERVER_DATA_TOOLTIP =
-            "Load data, features or sample information from a server";
-    final static public String LOAD_ATTRIBUTES_TOOLTIP =
-            "Load track attributes";
-    final static public String SAVE_IMAGE_TOOLTIP =
-            "Capture and save an image";
-    final static public String NEW_SESSION_TOOLTIP =
-            "Create a new session";
-    final static public String SAVE_SESSION_TOOLTIP =
-            "Save the current session";
-    final static public String SAVE_SESSION_AS_TOOLTIP =
-            "Save the current session to the specified file";
-    final static public String RESTORE_SESSION_TOOLTIP =
-            "Reload the named session";
-    final static public String EXIT_TOOLTIP =
-            "Exit the application";
-    final static public String EXPORT_REGION_TOOLTIP =
-            "Allows currently selected regions to be exported to a file";
-    final static public String IMPORT_REGION_TOOLTIP =
-            "Allows previously exported regions to be reloaded";
-    final static public String CLEAR_REGION_TOOLTIP =
-            "Clear all regions of interest";
-
-    // Edit Menu Item Tooltips
-    final static public String CHANGE_GENOME_TOOLTIP =
-            "Switch the current genome";
+    final static public String CLICK_ITEM_TO_EDIT_TOOLTIP = "Click this item bring up its editor";
+    final static public String LOAD_TRACKS_TOOLTIP = "Load data, features or sample information";
+    final static public String LOAD_SERVER_DATA_TOOLTIP = "Load data, features or sample information from a server";
+    final static public String SAVE_IMAGE_TOOLTIP = "Capture and save an image";
+    final static public String NEW_SESSION_TOOLTIP = "Create a new session";
+    final static public String SAVE_SESSION_TOOLTIP = "Save the current session";
+    final static public String RESTORE_SESSION_TOOLTIP = "Reload the named session";
+    final static public String EXIT_TOOLTIP = "Exit the application";
+    final static public String EXPORT_REGION_TOOLTIP = "Allows currently selected regions to be exported to a file";
+    final static public String IMPORT_REGION_TOOLTIP = "Allows previously exported regions to be reloaded";
+    final static public String CHANGE_GENOME_TOOLTIP = "Switch the current genome";
     final static public String IMPORT_GENOME_TOOLTIP =
             "Create a user-defined genome and makes it available for use in the application";
     final static public String LOAD_GENOME_TOOLTIP =
@@ -80,53 +79,266 @@ public class UIConstants {
             "Removes user-defined genomes from the drop-down list";
     final static public String CLEAR_GENOME_CACHE_TOOLTIP =
             "Clears locally cached versions of IGV hosted genomes.";
+    final static public String PREFERENCE_TOOLTIP = "Set user specific preferences";
+    final static public String SHOW_ATTRIBUTE_DISPLAY_TOOLTIP = "Show or hide the attribute display";
+    final static public String REFRESH_TOOLTIP = "Refresh the application's display";
+    final static public String SHOW_HEATMAP_LEGEND_TOOLTIP = "IGVPanel or edit color legends and scales";
+    final static public String SELECT_DISPLAYABLE_ATTRIBUTES_TOOLTIP = "Customize attribute display to show only checked attributes";
+    final static public String DIRECT_DRAW_DISABLED_TOOLTIP = "Checked this item to prevent the use of direct draw" +
+            " when rendering images - Uncheck it to use the default system behavior";
+    final static public String SORT_TRACKS_TOOLTIP = "Sort tracks by attribute value";
+    final static public String GROUP_TRACKS_TOOLTIP = "Group tracks";
+    final static public String FILTER_TRACKS_TOOLTIP = "Filter tracks by attribute value";
+    final static public String SET_DEFAULT_TRACK_HEIGHT_TOOLTIP = "Set the height for all tracks";
+    final static public String FIT_DATA_TO_WINDOW_TOOLTIP = "Resizes all track heights in order to make all tracks visible in " +
+            "their display with no vertical scrolling";
+    final static public String HELP_TOOLTIP = "Open web help page";
+    final static public String TUTORIAL_TOOLTIP = "Open tutorial web page";
+    final static public String ABOUT_TOOLTIP = "Display application information";
+    final static public String MACRO_SNAPSHOTS = "Macro Snapshots";
+    final static public String RESET_FACTORY_TOOLTIP = "Restores all user preferences to their default settings.";
 
-    // View Menu Item Tooltips
-    final static public String PREFERENCE_TOOLTIP =
-            "Set user specific preferences";
-    final static public String SHOW_ATTRIBUTE_DISPLAY_TOOLTIP =
-            "Show or hide the attribute display";
-    final static public String ENABLE_REGIONS_OF_INTEREST_TOOLTIP =
-            "Enable the \"Region of Interest\" tool";
-    final static public String REFRESH_TOOLTIP =
-            "Refresh the application's display";
-    final static public String SHOW_HEATMAP_LEGEND_TOOLTIP =
-            "View or edit color legends and scales";
-    final static public String SELECT_DISPLAYABLE_ATTRIBUTES_TOOLTIP =
-            "Customize attribute display to show only checked attributes";
-    final static public String USE_IMAGE_CACHING_TOOLTIP =
-            "Use image caching to improve display performance";
-    final static public String DIRECT_DRAW_DISABLED_TOOLTIP =
-            "Checked this item to prevent the use of direct draw" +
-                    " when rendering images - Uncheck it to use the default system behavior";
-
-    // Tracks Menu Item Tooltips
-    final static public String SORT_TRACKS_TOOLTIP =
-            "Sort tracks by attribute value";
-    final static public String GROUP_TRACKS_TOOLTIP =
-            "Group tracks";
-    final static public String FILTER_TRACKS_TOOLTIP =
-            "Filter tracks by attribute value";
-    final static public String RESET_DEFAULT_TRACK_HEIGHT_TOOLTIP =
-            "Reset all track to the default track height";
-    final static public String SET_DEFAULT_TRACK_HEIGHT_TOOLTIP =
-            "Set the height for all tracks";
-    final static public String FIT_DATA_TO_WINDOW_TOOLTIP =
-            "Resizes all track heights in order to make all tracks visible in " +
-                    "their display with no vertical scrolling";
-
-    // Help Menu Item Tooltips
-    final static public String HELP_TOOLTIP =
-            "Open web help page";
-    final static public String TUTORIAL_TOOLTIP =
-            "Open tutorial web page";
-    final static public String ABOUT_TOOLTIP =
-            "Display application information";
-
-    // Tool Menu
-    final static public String MACRO_SNAPSHOTS =
-            "Macro Snapshots";
-    final static public String RESET_FACTORY_TOOLTIP =
-            "Restores all user preferences to their default settings.";
-}
+    private static File DEFAULT_USER_DIRECTORY;
+    private static File DEFAULT_IGV_DIRECTORY;
+    private static File GENOME_CACHE_DIRECTORY;
+    private static File IGV_TEMP_DIRECTORY;
+    public static final String IGV_DIR_USERPREF = "igvDir";
+
+    public static int getDoubleClickInterval() {
+
+        if (doubleClickInterval < 0) {
+            try {
+                Number obj = (Number) Toolkit.getDefaultToolkit().getDesktopProperty("awt.multiClickInterval");
+                doubleClickInterval = obj.intValue();
+            } catch (Exception e) {
+                log.info("Error retrieving doubleClickInterval", e);
+                doubleClickInterval = 500;
+            }
+
+        }
+        return doubleClickInterval;
+    }
+
+    public static synchronized File getUserDirectory() {
+        if (DEFAULT_USER_DIRECTORY == null) {
+            DEFAULT_USER_DIRECTORY = FileSystemView.getFileSystemView().getDefaultDirectory();
+        }
+        return DEFAULT_USER_DIRECTORY;
+    }
+
+    public static File getIgvDirectory() {
+
+        // Hack for know Java bug
+        if (System.getProperty("os.name").equals("Windows XP")) {
+            try {
+                Runtime.getRuntime().exec("attrib -r \"" + getUserDirectory().getAbsolutePath() + "\"");
+            } catch (IOException e) {
+                // Oh well, we tried
+
+            }
+        }
+
+        if (DEFAULT_IGV_DIRECTORY == null)
+
+        {
+
+            // See if an override is stored in preferences.  Try to create a directory if it is.  If there is an
+            // error (the log is likely not available yet) and try to use the standard directory
+            try {
+                Preferences prefs = Preferences.userNodeForPackage(UIConstants.class);
+                String userDir = prefs.get(IGV_DIR_USERPREF, null);
+                if (userDir != null) {
+                    DEFAULT_IGV_DIRECTORY = new File(userDir);
+                    if (!DEFAULT_IGV_DIRECTORY.exists()) {
+                        DEFAULT_IGV_DIRECTORY = null;
+                        prefs.remove(IGV_DIR_USERPREF);
+                    }
+                }
+            } catch (Exception e) {
+                Preferences prefs = Preferences.userNodeForPackage(UIConstants.class);
+                prefs.remove(IGV_DIR_USERPREF);
+                System.err.println("Error creating user directory");
+                e.printStackTrace();
+            }
 
+            // No overide, try the default place
+            if (DEFAULT_IGV_DIRECTORY == null) {
+                String userHomeString = System.getProperty("user.home");
+                File rootDir = new File(userHomeString);
+                if (!(rootDir.exists() && canWrite(rootDir))) {
+                    rootDir = getUserDirectory();
+                }
+                if (UIConstants.IS_MAC) {
+                    DEFAULT_IGV_DIRECTORY = new File(rootDir, ".igv");
+                } else {
+                    DEFAULT_IGV_DIRECTORY = new File(rootDir, "igv");
+                }
+                if (!DEFAULT_IGV_DIRECTORY.exists()) {
+                    try {
+                        DEFAULT_IGV_DIRECTORY.mkdir();
+                    }
+                    catch (Exception e) {
+                        System.err.println("Error creating user directory");
+                        e.printStackTrace();
+                    }
+                }
+            }
+
+            // The IGV directory either doesn't exist or isn't writeable.  This situation can arise with Windows Vista
+            // and Windows 7 due to a Java bug (http://bugs.sun.com/view_bug.do?bug_id=4787931)
+            if (!(DEFAULT_IGV_DIRECTORY.exists() && DEFAULT_IGV_DIRECTORY.canRead() && canWrite(DEFAULT_IGV_DIRECTORY))) {
+                int option = JOptionPane.showConfirmDialog(null,
+                        "<html>The default IGV directory (" + DEFAULT_IGV_DIRECTORY + ") " +
+                                "cannot be accessed.  Click Yes to choose a new folder or No to exit.<br>" +
+                                "This folder will be used to store user preferences and cached genomes.",
+                        "IGV Directory Error", JOptionPane.YES_NO_OPTION);
+
+                if (option == JOptionPane.YES_OPTION) {
+                    final JFileChooser fc = new JFileChooser();
+                    fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+                    int retValue = fc.showOpenDialog(null);
+                    if (retValue == JFileChooser.APPROVE_OPTION) {
+                        DEFAULT_IGV_DIRECTORY = fc.getSelectedFile();
+                        Preferences prefs = Preferences.userNodeForPackage(UIConstants.class);
+                        prefs.put(IGV_DIR_USERPREF, DEFAULT_IGV_DIRECTORY.getAbsolutePath());
+                    }
+                }
+            }
+
+
+            if (!DEFAULT_IGV_DIRECTORY.canRead()) {
+                throw new DataLoadException("Cannot read from user directory", DEFAULT_IGV_DIRECTORY.getAbsolutePath());
+            } else if (!canWrite(DEFAULT_IGV_DIRECTORY)) {
+                throw new DataLoadException("Cannot write to user directory", DEFAULT_IGV_DIRECTORY.getAbsolutePath());
+            }
+
+            System.out.println("IGV directory: " + DEFAULT_IGV_DIRECTORY);
+
+        }
+
+
+        return DEFAULT_IGV_DIRECTORY;
+    }
+
+    private static boolean canWrite(File directory) {
+        // There are bugs in Java window (targe fix is Java 7).  The only way to know for sure is to try to write something
+        if (IS_WINDOWS) {
+            File testFile = null;
+            try {
+                testFile = new File(directory, "igv332415dsfjdsklt.testfile");
+                if (testFile.exists()) {
+                    testFile.delete();
+                }
+                testFile.deleteOnExit();
+                testFile.createNewFile();
+                return testFile.exists();
+            } catch (IOException e) {
+                return false;
+            } finally {
+                if (testFile.exists()) {
+                    testFile.delete();
+                }
+            }
+        } else {
+            return directory.canWrite();
+        }
+
+    }
+
+    public static File getGenomeCacheDirectory() {
+        if (GENOME_CACHE_DIRECTORY == null) {
+            GENOME_CACHE_DIRECTORY = new File(getIgvDirectory(), Globals.GENOME_CACHE_FOLDER_NAME);
+            if (!getGenomeCacheDirectory().exists()) {
+                getGenomeCacheDirectory().mkdir();
+            }
+            if (!getGenomeCacheDirectory().canRead()) {
+                throw new DataLoadException("Cannot read from user directory", getGenomeCacheDirectory().getAbsolutePath());
+            } else if (!getGenomeCacheDirectory().canWrite()) {
+                throw new DataLoadException("Cannot write to user directory", getGenomeCacheDirectory().getAbsolutePath());
+            }
+        }
+        return GENOME_CACHE_DIRECTORY;
+    }
+
+    public static File getTmpDirectory() {
+        if (IGV_TEMP_DIRECTORY == null) {
+            String tmpDir = (System.getProperty("java.io.tmpdir"));
+            IGV_TEMP_DIRECTORY = new File(tmpDir);
+            if (!getTmpDirectory().exists()) {
+                getTmpDirectory().mkdir();
+            }
+            if (!IGV_TEMP_DIRECTORY.canRead()) {
+                throw new DataLoadException("Cannot read from user directory", getTmpDirectory().getAbsolutePath());
+            } else if (!IGV_TEMP_DIRECTORY.canWrite()) {
+                throw new DataLoadException("Cannot write to user directory", getTmpDirectory().getAbsolutePath());
+            }
+
+        }
+        return IGV_TEMP_DIRECTORY;
+    }
+
+
+    /**
+     * Field description
+     */
+    final public static String OVERWRITE_SESSION_MESSAGE =
+            "<html>Opening a session will unload all current data. " + "<br>Are you sure you wish to continue?";
+    /**
+     * Field description
+     */
+    final public static String NEW_SESSION_MESSAGE =
+            "<html>Creating a new session will unload all current data. " + "<br>Are you sure you wish to continue?";
+    final public static String CANNOT_ACCESS_SERVER_GENOME_LIST = "The Genome server is currently inaccessible.";
+    final public static String INVALID_SERVER_GENOME_LIST_HEADER = "Genomes cannot be retrieved from the server. " + "The server-side genome list is invalid!";
+    // Session Folder
+
+    /**
+     * Field description
+     */
+    final public static int NUMBER_OF_RECENT_SESSIONS_TO_LIST = 3;
+    /**
+     * Field description
+     */
+    final public static String DEFAULT_SESSION_FILE = "igv_session" + Globals.SESSION_FILE_EXTENSION;
+    // URLs
+    /**
+     * Field description
+     */
+    final static public String SERVER_BASE_URL = "http://www.broadinstitute.org/";
+    /**
+     * Field description
+     */
+    final public static String IGV_LOG_SERVER_URL = SERVER_BASE_URL + "igv/LogServlet";
+    // Colors
+    // float[] hsb = Color.RGBtoHSB(255, 255, 210, null);
+    /**
+     * Field description
+     */
+    final static public Color LIGHT_YELLOW = new Color(255, 244, 201);
+    /**
+     * Field description
+     */
+    final public static Color VERY_LIGHT_GRAY = new Color(238, 239, 240);
+    /**
+     * Field description
+     */
+    public static Color NO_DATA_COLOR = new Color(200, 200, 200, 150);
+    // GENOME
+    /**
+     * Field description
+     */
+    final static public String IMPORT_GENOME_LIST_MENU_ITEM = "Import Genome...";
+    /**
+     * Field description
+     */
+    final static public String LOAD_GENOME_LIST_MENU_ITEM = "Load Genome...";
+    /**
+     * Field description
+     */
+    final static public String REMOVE_GENOME_LIST_MENU_ITEM = "Remove Imported Genomes...";
+    /**
+     * Field description
+     */
+    final static public String GENOME_LIST_SEPARATOR = "--SEPARATOR--";
+
+}
diff --git a/src/org/broad/igv/ui/UserDefinedGenomeCheckList.java b/src/org/broad/igv/ui/UserDefinedGenomeCheckList.java
index 88856be..86b9653 100644
--- a/src/org/broad/igv/ui/UserDefinedGenomeCheckList.java
+++ b/src/org/broad/igv/ui/UserDefinedGenomeCheckList.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/ViewContext.java b/src/org/broad/igv/ui/ViewContext.java
deleted file mode 100644
index 7a47365..0000000
--- a/src/org/broad/igv/ui/ViewContext.java
+++ /dev/null
@@ -1,896 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.ui;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
-import org.broad.igv.PreferenceManager;
-import org.broad.igv.feature.Chromosome;
-import org.broad.igv.feature.Genome;
-import org.broad.igv.feature.GenomeManager;
-import org.broad.igv.feature.GenomeDescriptor;
-import org.broad.igv.sam.AlignmentTrack;
-import org.broad.igv.track.Track;
-import org.broad.igv.track.TrackGroup;
-import org.broad.igv.ui.panel.DataPanel;
-import org.broad.igv.ui.panel.DragEventManager;
-import org.broad.igv.ui.panel.TrackSetScrollPane;
-import org.broad.igv.ui.util.MessageUtils;
-
-import javax.swing.*;
-import java.awt.*;
-import java.awt.event.ComponentAdapter;
-import java.awt.event.ComponentEvent;
-import java.io.IOException;
-import java.text.NumberFormat;
-import java.util.*;
-import java.util.List;
-
-/**
- * @author jrobinso
- */
-public class ViewContext {
-
-    private static Logger log = Logger.getLogger(ViewContext.class);
-
-
-    private String genomeId;
-
-    Genome genome;
-
-    /**
-     * The nominal viewport width in pixels.
-     */
-    public int binsPerTile = 700;
-
-    int dataPanelWidth = 700;
-
-    /**
-     * The chromosome currently in view
-     */
-    private String chrName = "chrAll";
-
-    /**
-     * The minimum zoom level for the current screen size + chromosome combination.
-     */
-    private int minZoom = 0;
-
-    /**
-     * The current zoom level.  Zoom level -1 corresponds to the whole
-     * genome view (chromosome "all")
-     */
-    private int zoom = minZoom;
-    /**
-     * The maximum zoom level.  Set to prevent integer overflow.  This is a function
-     * of chromosom length.
-     */
-    public static int maxZoom = 23;
-    /**
-     * The number of tiles for this zoom level, = 2^zoom
-     */
-    private int nTiles = 1;
-    /**
-     * The maximum virtual pixel value.
-     */
-    private double maxPixel;
-    /**
-     * The origin in bp
-     */
-    private double origin = 0;
-
-    /**
-     * The location (x axis) locationScale in base pairs / virtual pixel
-     */
-    private double locationScale;
-
-    private boolean locationScaleValid = false;
-    /**
-     * Indicates if the data is stretched to fill the window.  This happens at low
-     * resolution views when drawing at the resolution of the zoom level would
-     * only partially fill the screen.
-     */
-    private boolean stretched = false;
-    /**
-     * Regions of Interest
-     */
-    private LinkedHashMap<String, LinkedHashSet<RegionOfInterest>> regionsOfInterest =
-            new LinkedHashMap<String, LinkedHashSet<RegionOfInterest>>();
-
-    /**
-     * Constructs ...
-     */
-    public ViewContext() {
-        String lastChromosomeViewed = PreferenceManager.getInstance().getLastChromosomeViewed();
-
-        if ((lastChromosomeViewed == null) || lastChromosomeViewed.trim().equals("")) {
-            this.chrName = getHomeChr();
-        } else {
-            this.chrName = lastChromosomeViewed;
-
-        }
-    }
-
-    /**
-     * Return the "home chromosome" for the current genome.  This should probably be a method
-     * on the Genome class.
-     *
-     * @return
-     */
-    public String getHomeChr() {
-        return genome == null ? IGVConstants.CHR_ALL : genome.getHomeChromosome();
-    }
-
-    /**
-     * Compute the maximum zoom level, which is a function of chromosome length.
-     */
-    private void computeMaxZoom() {
-
-        // Compute max zoom.  Assume window size @ max zoom of ~ 50 bp
-        if (genome != null && chrName != null && genome.getChromosome(chrName) != null) {
-            if (chrName.equals(IGVConstants.CHR_ALL)) {
-                maxZoom = 0;
-            } else {
-                int chrLength = genome.getChromosome(chrName).getLength();
-
-                maxZoom = (int) (Math.log(chrLength / 50.0) / log2) + 1;
-            }
-
-            if (zoom > maxZoom) {
-                setZoom(maxZoom);
-            }
-        }
-
-
-        // TODO -- fire chromosome changed event rather than this
-    }
-
-    private void setZoom(int newZoom) {
-        zoom = Math.min(maxZoom, newZoom);
-        nTiles = (int) Math.pow(2, Math.max(minZoom, zoom));
-        maxPixel = getTilesTimesBinsPerTile();
-        invalidateLocationScale();
-
-        // TODO -- do this with events,
-        if (IGVMainFrame.hasInstance()) {
-            IGVMainFrame.getInstance().repaintStatusAndZoomSlider();
-        }
-
-    }
-
-    /**
-     * Method description
-     *
-     * @param increment
-     */
-    public void incrementZoom(int increment) {
-        zoomAndCenter((int) zoom + increment);
-    }
-
-    /**
-     * Method description
-     *
-     * @param newZoom
-     */
-    public void zoomAndCenterAdjusted(int newZoom) {
-        zoomAndCenter(minZoom + newZoom);
-    }
-
-    /**
-     * Method description
-     *
-     * @param newZoom
-     */
-    public void zoomAndCenter(int newZoom) {
-
-        // Zoom but remain centered about current center
-        double currentCenter = origin + ((dataPanelWidth / 2) * getScale());
-
-        zoomTo(newZoom, currentCenter);
-    }
-
-    /**
-     * Method description
-     *
-     * @param newZoom
-     * @param newCenter
-     */
-    public void zoomTo(final int newZoom, final double newCenter) {
-
-        if (chrName.equals(IGVConstants.CHR_ALL)) {
-            chrName = getHomeChr();
-        }
-
-        if (chrName.equals(IGVConstants.CHR_ALL)) {
-
-            // DISABLE ZOOMING FOR GENOME VIEW
-            // Translate the location to chromosome number
-            jumpToChromosomeForGenomeLocation(newCenter);
-            IGVMainFrame.getInstance().chromosomeChangeEvent();
-        } else {
-            if (zoom != newZoom) {
-
-                setZoom(newZoom);
-                computeLocationScaleImmediately();
-
-                double newLocationScale = getScale();
-
-                // Adjust origin so newCenter is centered
-                double newOrigin = Math.round(
-                        newCenter - ((dataPanelWidth / 2) * newLocationScale));
-
-                setOrigin(newOrigin);
-
-            }
-        }
-        // This is a hack,  this is not a drag event but is a "jump"
-        DragEventManager.getInstance().dragStopped();
-
-    }
-
-    private void jumpToChromosomeForGenomeLocation(double locationMB) {
-        double startMB = 0;
-
-        for (String chr : getGenome().getChromosomeNames()) {
-            double endMB = startMB + getGenome().getChromosome(chr).getLength() / 1000.0;
-
-            if ((locationMB > startMB) && (locationMB <= endMB)) {
-
-                // this.jumpTo(chr, -1, -1);
-                this.setChromosomeName(chr);
-                break;
-            }
-
-            startMB = endMB;
-        }
-    }
-
-    public void pageLeft() {
-        shiftOriginPixels(-dataPanelWidth);
-    }
-
-    public void pageRight() {
-        shiftOriginPixels(dataPanelWidth);
-    }
-
-    /**
-     * Method description
-     *
-     * @param delta
-     */
-    public void shiftOriginPixels(double delta) {
-        double shiftBP = delta * getScale();
-        setOrigin(origin + shiftBP);
-    }
-
-    public void snapToGrid() {
-        setOrigin(Math.round(origin));
-    }
-
-    /**
-     * Method description
-     *
-     * @param chrLocation
-     */
-    public void centerOnLocation(String chr, double chrLocation) {
-        if (!chrName.equals(chr)) {
-            chrName = chr;
-            computeMaxZoom();
-            if (zoom > maxZoom) {
-                setZoom(maxZoom);
-            }
-        }
-        centerOnLocation(chrLocation);
-    }
-
-    /**
-     * Method description
-     *
-     * @param chrLocation
-     */
-    public void centerOnLocation(double chrLocation) {
-        double windowWidth = (dataPanelWidth * getScale()) / 2;
-
-        setOrigin(Math.round(chrLocation - windowWidth));
-    }
-
-    public boolean windowAtEnd() {
-        double windowLengthBP = dataPanelWidth * getScale();
-        return origin + windowLengthBP + 1 > getChromosomeLength();
-
-    }
-
-
-    /* Keep origin within data range */
-    /**
-     * Method description
-     *
-     * @param newOrigin
-     */
-    public void setOrigin(double newOrigin) {
-        int windowLengthBP = (int) (dataPanelWidth * getScale());
-
-        origin = Math.max(0, Math.min(newOrigin, getChromosomeLength() - windowLengthBP));
-
-        for (TrackSetScrollPane sp : IGVMainFrame.getInstance().getTrackSetScrollPanes()) {
-            preloadTrackData(sp.getDataPanel());
-        }
-
-        // If zoomed in sufficiently track the center position
-        if (locationScale < 10) {
-            IGVMainFrame.getInstance().setStatusBarMessage(chrName + ":" + ((int) getCenter() + 1));
-        }
-
-        // Repaint
-        IGVMainFrame.getInstance().repaintDataAndHeaderPanels();
-        IGVMainFrame.getInstance().repaintStatusAndZoomSlider();
-    }
-
-    static double log2 = Math.log(2);
-
-    /**
-     * Method description
-     *
-     * @param chr
-     * @param start
-     * @param end
-     */
-    public void jumpTo(String chr, int start, int end) {
-
-        if (log.isDebugEnabled()) {
-            log.debug("Jump to: " + chr + ":" + start + "-" + end);
-        }
-
-        // Switch chromosomes if not null
-        if (chr != null) {
-            if (genome.getChromosome(chr) == null && !chr.contains(IGVConstants.CHR_ALL)) {
-                JOptionPane.showMessageDialog(IGVMainFrame.getInstance(),
-                        chr + " is not a valid chromosome.");
-                return;
-            }
-            setChromosomeName(chr);
-        }
-
-        if (start >= 0) {
-
-            // Estmate zoom level
-            int z = (int) (Math.log(getChromosomeLength() / (end - start)) / log2) + 1;
-
-            if (z != this.zoom) {
-                zoom = Math.min(maxZoom, Math.max(minZoom, z));
-                nTiles = (int) Math.pow(2, zoom);
-                maxPixel = getTilesTimesBinsPerTile();
-            }
-
-            // overide computed scale
-            int w = dataPanelWidth;
-            if (log.isDebugEnabled()) {
-                log.debug("W = " + w);
-            }
-
-            setLocationScale(((double) (end - start)) / w);
-
-            origin = start;
-        }
-
-        for (TrackSetScrollPane sp : IGVMainFrame.getInstance().getTrackSetScrollPanes()) {
-            preloadTrackData(sp.getDataPanel());
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug("Data panel width = " + dataPanelWidth);
-            log.debug("New start = " + (int) getOrigin());
-            log.debug("New end = " + (int) getEnd());
-            log.debug("New center = " + (int) getCenter());
-        }
-
-        // Repaint
-        IGVMainFrame.getInstance().repaintDataAndHeaderPanels();
-        IGVMainFrame.getInstance().repaintStatusAndZoomSlider();
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public double getOrigin() {
-        return origin;
-    }
-
-    public double getCenter() {
-        return origin + getScale() * dataPanelWidth / 2;
-    }
-
-    public double getEnd() {
-        return origin + getScale() * dataPanelWidth;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public int getZoom() {
-        return zoom;
-    }
-
-    /**
-     * Return the maximum zoom level
-     *
-     * @return
-     */
-    public int getMaxZoom() {
-        return maxZoom;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public int getAdjustedZoom() {
-        return zoom - minZoom;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public int getNTiles() {
-        return nTiles;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public double getMaxPixel() {
-        return maxPixel;
-    }
-
-    /**
-     * Method description
-     *
-     * @param panel
-     */
-    public void setDataPanel(DataPanel panel) {
-
-        if (panel != null) {
-
-            panel.addComponentListener(new ComponentAdapter() {
-
-                @Override
-                public void componentResized(ComponentEvent e) {
-                    int w = e.getComponent().getWidth();
-                    if (w != dataPanelWidth) {
-                        dataPanelWidth = w;
-                        invalidateLocationScale();
-                    }
-                }
-            });
-        }
-    }
-
-    /**
-     * Method description
-     *
-     * @param name
-     */
-    public void setChrName(String name) {
-        this.setChromosomeName(name);
-    }
-
-    /**
-     * @param name
-     * @param force
-     * @ deprecated, replace with calls to setChrName();
-     */
-    public void setChromosomeName(String name, boolean force) {
-
-        if ((chrName == null) || !name.equals(chrName) || force) {
-            chrName = name;
-            origin = 0;
-            setZoom(0);
-            computeMaxZoom();
-        }
-    }
-
-    /**
-     * @param name
-     * @ deprecated, replace with calls to setChrName();
-     */
-    public void setChromosomeName(String name) {
-        setChromosomeName(name, false);
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public String getChrName() {
-        return chrName;
-    }
-
-    public String getNextChrName(String chr) {
-        List<String> chrList = genome.getChromosomeNames();
-        for (int i = 0; i < chrList.size() - 1; i++) {
-            if (chrList.get(i).equals(chr)) {
-                return chrList.get(i + 1);
-            }
-        }
-        return null;
-    }
-
-    public String getPrevChrName(String chr) {
-        List<String> chrList = genome.getChromosomeNames();
-        for (int i = chrList.size() - 1; i > 0; i--) {
-            if (chrList.get(i).equals(chr)) {
-                return chrList.get(i - 1);
-            }
-        }
-        return null;
-    }
-
-    // TODO -- this parameter shouldn't be stored here.  Maybe in a specialized
-    // layout manager?
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public int getDataPanelWidth() {
-        return dataPanelWidth;
-    }
-
-    /**
-     * Return the current locationScale in base pairs / pixel
-     *
-     * @return
-     */
-    public double getScale() {
-        if ((locationScale == 0) || !locationScaleValid) {
-            computeLocationScaleImmediately();
-        }
-
-        if (locationScale < 0) {
-            System.err.println("Negative scale");
-        }
-
-        return locationScale;
-    }
-
-    /**
-     * Method description
-     */
-    public void invalidateLocationScale() {
-        //Thread.dumpStack();
-        locationScaleValid = false;
-    }
-
-    public void computeLocationScaleImmediately() {
-
-        if (genome != null) {
-
-            computeMinZoom();
-
-            double virtualPixelSize = getTilesTimesBinsPerTile();
-
-            stretched = virtualPixelSize < dataPanelWidth;
-
-            double nPixel = Math.max(virtualPixelSize, dataPanelWidth);
-
-            setLocationScale(((double) getChromosomeLength()) / nPixel);
-        }
-    }
-
-    /**
-     * Compute the minimum zoom level for the data panel width.  This is defined as the maximum
-     * zoom level for which all data bins will fit in the window without loss of
-     * data,  i.e. the maximum zoom level for which nBins < nPixels.  The number
-     * of bins is defined as
-     * nBins =  2^z
-     * so minZoom is the value z such that nBins < dataPanelWidth
-     */
-    private void computeMinZoom() {
-        if (this.chrName.equals(IGVConstants.CHR_ALL)) {
-            minZoom = 0;
-        } else {
-            minZoom = Math.max(0, (int) (Math.log((dataPanelWidth / binsPerTile)) / log2));
-
-            if (zoom < minZoom) {
-                zoom = minZoom;
-                nTiles = (int) Math.pow(2, zoom);
-                maxPixel = getTilesTimesBinsPerTile();
-            }
-        }
-
-    }
-
-    /**
-     * Return the chromosome position corresponding to the pixel index.  The
-     * pixel index is the pixel "position" translated by -origin.
-     *
-     * @param pixelIndex
-     * @return
-     */
-    public double getChromosomePosition(int pixelIndex) {
-        return origin + (getScale() * pixelIndex);
-    }
-
-    /**
-     * Return the pixel position corresponding to the chromosomal position.
-     *
-     * @param chromosomePosition
-     * @return
-     */
-    public int getPixelPosition(double chromosomePosition) {
-        return (int) ((chromosomePosition - origin) / getScale());
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public Chromosome getChromosome() {
-        if (genome == null) {
-            log.error("Genome not loaded!");
-            return null;
-        }
-
-        return genome.getChromosome(chrName);
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public int getChromosomeLength() {
-        
-        if(genome == null) {
-            return 1;
-        }
-
-        if (chrName.equals("All")) {
-
-            // TODO -- remove the hardcoded unit divider ("1000")
-            return (int) (genome.getLength() / 1000);
-
-            // return genome.getLength();
-        } else {
-            if (getChromosome() == null) {
-                System.out.println("Null chromosome: " + chrName);
-                if (genome == null) {
-                    return 1;
-                } else {
-                    return genome.getChromosomes().iterator().next().getLength();
-                }
-            }
-
-            return getChromosome().getLength();
-        }
-    }
-
-    // /////////////////////////////////////////////////////////////////////////
-    // Genome methods /////////////////////////////////////////////////////////
-    /**
-     * Attempt to switch genomes to newGenome.  Return the actual genome, which
-     * might differ if the load of newGenome fails.
-     *
-     * @param newGenome
-     */
-    public String setGenomeId(String newGenome) {
-
-        if (log.isDebugEnabled()) {
-            log.debug("Setting genome id: " + newGenome);
-        }
-
-        boolean startUp = (genomeId == null);
-        boolean loadFailed = false;
-
-        genomeId = newGenome;
-        if (!GenomeManager.getInstance().isGenomeLoaded(genomeId)) {
-            try {
-                GenomeManager.getInstance().findGenomeAndLoad(genomeId);
-            } catch (IOException e) {
-                log.error("Error loading genome: " + genomeId, e);
-                loadFailed = true;
-                MessageUtils.showMessage("Load of genome: " + genomeId + " failed.");
-            }
-        }
-        genome = GenomeManager.getInstance().getGenome(genomeId);
-
-        if (genome == null || loadFailed) {
-            GenomeDescriptor defaultDesc  = GenomeManager.getInstance().getDefaultGenomeDescriptor();
-            String msg = "Could not locate genome: " + genomeId + ".  Loading " + defaultDesc.getName();
-            MessageUtils.showMessage(msg);
-            log.error("Could not locate genome: " + genomeId + ".  Loading " + defaultDesc.getName());
-
-            // The previously used genome is unavailable.   Load hg18, we are assuming that
-            // this is always available.  // TODO -- permit IGV starting with no selected genome
-            genomeId = defaultDesc.getId();
-            try {
-                GenomeManager.getInstance().findGenomeAndLoad(genomeId);
-            } catch (IOException e) {
-                log.error("Error loading genome: " + genomeId, e);
-                MessageUtils.showMessage("<html>Load of genome: " + genomeId + " failed." +
-                        "<br>IGV is in an ustable state and will be closed." +
-                        "<br>Please report this error to igv-help at broadinstitute.org");
-                System.exit(-1);
-            }
-            genome = GenomeManager.getInstance().getGenome(genomeId);
-            PreferenceManager.getInstance().setDefaultGenome(genomeId);
-            this.setChrName(getHomeChr());
-        } else if (!startUp) {
-            // We don't know what chromosomes the new genome has, set to "all" (whole genome)
-            this.setChrName(getHomeChr());
-        }
-        computeMaxZoom();
-        invalidateLocationScale();
-
-        return genomeId;
-    }
-
-    /**
-     * Version for command line tools.
-     *
-     * @param newGenome
-     */
-    public void setGenomeIdHeadless(String newGenome) {
-
-
-        genomeId = newGenome;
-        genome = GenomeManager.getInstance().getGenome(genomeId);
-
-
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public String getGenomeId() {
-        return genomeId;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public Genome getGenome() {
-        return genome;
-    }
-
-    /**
-     * Return the collection of chromosome names for the currently loaded
-     * genome
-     *
-     * @return
-     */
-    public Collection<String> getChromosomeNames() {
-        return genome.getChromosomeNames();
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public boolean isStretched() {
-        return stretched;
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public double getTilesTimesBinsPerTile() {
-        return (double) nTiles * (double) binsPerTile;
-    }
-
-    /**
-     * Get the UCSC style locus string corresponding to the current view.  THe UCSC
-     * conventions are followed for coordinates,  specifically the internal representation
-     * is "zero" based (first base is numbered 0) but the display representation is
-     * "one" based (first base is numbered 1).   Consequently 1 is added to the
-     * computed positions.
-     *
-     * @return
-     */
-    public String getCurrentLocusString() {
-
-        if (zoom == 0) {
-            return getChrName();
-        } else {
-
-            int start = 0;
-            int end = getDataPanelWidth();
-            int startLoc = (int) getChromosomePosition(start) + 1;
-            int endLoc = (int) getChromosomePosition(end);
-            String startStr = NumberFormat.getInstance().format(startLoc);
-            String endStr = NumberFormat.getInstance().format(endLoc);
-            String position = getChrName() + ":" + startStr + "-" + endStr;
-
-            return position;
-        }
-
-    }
-
-    private void setLocationScale(double locationScale) {
-        this.locationScale = locationScale;
-        locationScaleValid = true;
-
-        if (log.isDebugEnabled()) {
-            log.debug("locationScale = " + locationScale);
-        }
-    }
-
-    /**
-     *
-     */
-    private void preloadTrackData(DataPanel dp) {
-
-        Rectangle visibleRect = dp.getVisibleRect();
-        int start = (int) origin;
-        int end = (int) (start + dp.getWidth() * locationScale);
-        int trackY = 0;
-        Collection<TrackGroup> groups = dp.getTrackGroups();
-
-        for (Iterator<TrackGroup> groupIter = groups.iterator(); groupIter.hasNext();) {
-            TrackGroup group = groupIter.next();
-            if (group.isVisible()) {
-                if (groups.size() > 1) {
-                    trackY += IGVConstants.groupGap;
-                }
-                for (Track track : group.getTracks()) {
-                    int trackHeight = track.getHeight();
-
-                    if (!(track instanceof AlignmentTrack) && visibleRect != null) {
-                        if (trackY > visibleRect.y + visibleRect.height) {
-                            break;
-                        } else if (trackY + trackHeight < visibleRect.y) {
-                            trackY += trackHeight;
-                            continue;
-                        }
-                    }
-
-
-                    if (track.isVisible()) {
-                        track.preloadData(chrName, start, end, zoom);
-
-                        trackY += track.getHeight();
-                    }
-                }
-            }
-
-        }
-    }
-}
diff --git a/src/org/broad/igv/ui/WaitCursorManager.java b/src/org/broad/igv/ui/WaitCursorManager.java
index cd9adbf..18b8f31 100644
--- a/src/org/broad/igv/ui/WaitCursorManager.java
+++ b/src/org/broad/igv/ui/WaitCursorManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,6 +22,7 @@ package org.broad.igv.ui;
 //~--- JDK imports ------------------------------------------------------------
 
 import java.awt.*;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -41,7 +42,7 @@ public class WaitCursorManager {
      * "removeWaitCursor" method.  The presence of a token in this list indicates that IGV is
      * in the wait state.
      */
-    static Set<CursorToken> tokens = new HashSet();
+    static Set<CursorToken> tokens = Collections.synchronizedSet(new HashSet());
 
     /**
      * The wait cursor, defined statically for convenience.
@@ -56,14 +57,12 @@ public class WaitCursorManager {
      *         the wait cursr.  This should be done in a finally block to insure removal.
      */
     public static CursorToken showWaitCursor() {
-        synchronized (tokens) {
-            IGVMainFrame.getInstance().getGlassPane().setVisible(true);
-            CursorToken token = new CursorToken();
-            tokens.add(token);
-            // Return a token representing this wait cursor set.  The token is used to release the
-            // wait cursor.
-            return token;
-        }
+        IGVMainFrame.getInstance().getGlassPane().setVisible(true);
+        CursorToken token = new CursorToken();
+        tokens.add(token);
+        // Return a token representing this wait cursor set.  The token is used to release the
+        // wait cursor.
+        return token;
     }
 
     /**
@@ -75,12 +74,9 @@ public class WaitCursorManager {
      * @param token
      */
     public static void removeWaitCursor(CursorToken token) {
-        synchronized (tokens) {
-            tokens.remove(token);
-            if (tokens.isEmpty()) {
-                IGVMainFrame.getInstance().getGlassPane().setVisible(false);
-            }
-
+        tokens.remove(token);
+        if (tokens.isEmpty()) {
+            IGVMainFrame.getInstance().getGlassPane().setVisible(false);
         }
     }
 
diff --git a/src/org/broad/igv/ui/ZoomSliderPanel.java b/src/org/broad/igv/ui/ZoomSliderPanel.java
index 590de33..84d6802 100644
--- a/src/org/broad/igv/ui/ZoomSliderPanel.java
+++ b/src/org/broad/igv/ui/ZoomSliderPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -26,7 +26,10 @@
  */
 package org.broad.igv.ui;
 
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.ui.util.IconFactory;
+import org.broad.igv.util.LongRunningTask;
+import org.broad.igv.util.NamedRunnable;
 
 import javax.swing.*;
 import javax.swing.event.MouseInputAdapter;
@@ -177,8 +180,9 @@ public class ZoomSliderPanel extends JPanel {
     }
 
     // TODO implement without reference to the frame singleton
+
     private ViewContext getViewContext() {
-        return IGVModel.getInstance().getViewContext();
+        return ViewContext.getInstance();
     }
 
     int toolZoom = -1;
@@ -214,14 +218,16 @@ public class ZoomSliderPanel extends JPanel {
                 setZoom(e);
                 repaint();
 
-                LongRunningTask.submit(new Runnable() {
-                    public void run() {
-                        getViewContext().zoomAndCenterAdjusted(toolZoom);
-                        toolZoom = -1;
-                    }
-                });
+                WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
+                try {
+                    getViewContext().zoomAndCenterAdjusted(toolZoom);
+                    toolZoom = -1;
+                } finally {
+                    WaitCursorManager.removeWaitCursor(token);
+                }
             }
 
+
             @Override
             public void mouseDragged(MouseEvent e) {
                 // Dragging zoom tool is disable.  Generates too many
diff --git a/src/org/broad/igv/ui/action/ClearGenomeCacheAction.java b/src/org/broad/igv/ui/action/ClearGenomeCacheAction.java
index fbb06c6..53557f7 100644
--- a/src/org/broad/igv/ui/action/ClearGenomeCacheAction.java
+++ b/src/org/broad/igv/ui/action/ClearGenomeCacheAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -16,7 +16,6 @@
  * FOREGOING.
  */
 
-
 /*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
@@ -27,10 +26,10 @@ package org.broad.igv.ui.action;
 
 import org.apache.log4j.Logger;
 import org.broad.igv.feature.GenomeManager;
-import org.broad.igv.ui.GuiUtilities;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.ui.IGVMainFrame;
-import org.broad.igv.ui.IGVModel;
 import org.broad.igv.ui.UIConstants;
+import org.broad.igv.ui.util.UIUtilities;
 
 import javax.swing.*;
 import java.awt.event.ActionEvent;
@@ -66,9 +65,9 @@ public class ClearGenomeCacheAction extends MenuAction {
                     JOptionPane.YES_NO_OPTION));
             if (option == JOptionPane.YES_OPTION) {
                 {
-                    final String genomeId = IGVModel.getInstance().getViewContext().getGenomeId();
+                    final String genomeId = ViewContext.getInstance().getGenomeId();
                     GenomeManager.getInstance().clearGenomeCache();
-                    GuiUtilities.invokeOnEventThread(new Runnable() {
+                    UIUtilities.invokeOnEventThread(new Runnable() {
                         public void run() {
                             IGVMainFrame.getInstance().rebuildGenomeDropdownList(null);
                             IGVMainFrame.getInstance().setGenomeId(genomeId);
diff --git a/src/org/broad/igv/ui/action/ClearRegionsMenuAction.java b/src/org/broad/igv/ui/action/ClearRegionsMenuAction.java
index f94dbbe..91a8eed 100644
--- a/src/org/broad/igv/ui/action/ClearRegionsMenuAction.java
+++ b/src/org/broad/igv/ui/action/ClearRegionsMenuAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,10 +23,9 @@
 package org.broad.igv.ui.action;
 
 import org.apache.log4j.Logger;
-import org.broad.igv.ui.GuiUtilities;
 import org.broad.igv.ui.IGVMainFrame;
-import org.broad.igv.ui.IGVModel;
 import org.broad.igv.ui.UIConstants;
+import org.broad.igv.ui.util.UIUtilities;
 
 import javax.swing.*;
 import java.awt.event.ActionEvent;
@@ -48,7 +47,7 @@ public class ClearRegionsMenuAction extends MenuAction {
     @Override
     public void actionPerformed(ActionEvent e) {
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
                 int choice = JOptionPane.showConfirmDialog(
@@ -57,8 +56,8 @@ public class ClearRegionsMenuAction extends MenuAction {
                         "Clear Regions",
                         JOptionPane.YES_NO_OPTION);
                 if (choice == JOptionPane.YES_OPTION) {
-                    IGVModel.getInstance().clearRegionsOfInterest();
-                    IGVMainFrame.getInstance().repaint();
+                    mainFrame.getSession().clearRegionsOfInterest();
+                    mainFrame.repaint();
                 }
             }
         });
diff --git a/src/org/broad/igv/ui/action/Command.java b/src/org/broad/igv/ui/action/Command.java
index fc5fa1d..cc3c4d6 100644
--- a/src/org/broad/igv/ui/action/Command.java
+++ b/src/org/broad/igv/ui/action/Command.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/action/DisableDirectDrawAction.java b/src/org/broad/igv/ui/action/DisableDirectDrawAction.java
index 90ced82..4ab38d2 100644
--- a/src/org/broad/igv/ui/action/DisableDirectDrawAction.java
+++ b/src/org/broad/igv/ui/action/DisableDirectDrawAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/action/ExportRegionsMenuAction.java b/src/org/broad/igv/ui/action/ExportRegionsMenuAction.java
index 64483c7..1c2840e 100644
--- a/src/org/broad/igv/ui/action/ExportRegionsMenuAction.java
+++ b/src/org/broad/igv/ui/action/ExportRegionsMenuAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,9 +23,9 @@
 package org.broad.igv.ui.action;
 
 import org.apache.log4j.Logger;
-import org.broad.igv.ui.GuiUtilities;
 import org.broad.igv.ui.IGVMainFrame;
 import org.broad.igv.ui.UIConstants;
+import org.broad.igv.ui.util.UIUtilities;
 
 import java.awt.event.ActionEvent;
 
@@ -45,7 +45,7 @@ public class ExportRegionsMenuAction extends RegionsBaseMenuAction {
     @Override
     public void actionPerformed(ActionEvent e) {
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
                 importExportRegionsOfInterest(Direction.EXPORT);
diff --git a/src/org/broad/igv/ui/action/FilterTracksMenuAction.java b/src/org/broad/igv/ui/action/FilterTracksMenuAction.java
index 7d37819..a493dee 100644
--- a/src/org/broad/igv/ui/action/FilterTracksMenuAction.java
+++ b/src/org/broad/igv/ui/action/FilterTracksMenuAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -24,11 +24,11 @@ package org.broad.igv.ui.action;
 
 import org.broad.igv.track.AttributeManager;
 import org.broad.igv.track.Track;
-import org.broad.igv.track.TrackManager;
 import org.broad.igv.ui.IGVMainFrame;
 import org.broad.igv.ui.TrackFilter;
 import org.broad.igv.ui.TrackFilterPane;
 import org.broad.igv.ui.UIConstants;
+import org.broad.igv.ui.util.MessageUtils;
 import org.broad.igv.util.Filter;
 
 import javax.swing.*;
@@ -47,9 +47,6 @@ public class FilterTracksMenuAction extends MenuAction {
     //static Logger log = Logger.getLogger(FilterTracksMenuAction.class);
     IGVMainFrame mainFrame;
 
-    // Current track filter
-    private TrackFilter trackFilter;
-
     private JCheckBox showAllTracksFilterCheckBox = new JCheckBox();
 
     private JCheckBox matchAllCheckBox = new JCheckBox();
@@ -71,32 +68,26 @@ public class FilterTracksMenuAction extends MenuAction {
 
     private void doFilterTracks() {
 
-        boolean previousDisableFilterState =
-                showAllTracksFilterCheckBox.isSelected();
+        boolean previousDisableFilterState = showAllTracksFilterCheckBox.isSelected();
 
-        boolean previousMatchAllState =
-                matchAllCheckBox.isSelected();
+        boolean previousMatchAllState = matchAllCheckBox.isSelected();
 
-        List<String> uniqueAttributeKeys =
-                AttributeManager.getInstance().getAttributeKeys();
+        List<String> uniqueAttributeKeys = AttributeManager.getInstance().getAttributeKeys();
 
         // Sort the attribute keys if we have any
         if (uniqueAttributeKeys != null) {
-            Collections.sort(uniqueAttributeKeys,
-                    AttributeManager.getInstance().getAttributeComparator());
+            //Collections.sort(uniqueAttributeKeys, AttributeManager.getInstance().getAttributeComparator());
         } else // If we have no attribute we can't display the
             // track filter dialog so say so and return
-            if (uniqueAttributeKeys == null ||
-                    uniqueAttributeKeys.isEmpty()) {
+            if (uniqueAttributeKeys == null || uniqueAttributeKeys.isEmpty()) {
 
-                JOptionPane.showMessageDialog(mainFrame,
-                        "No attributes found to use in a filter");
+                MessageUtils.showMessage("No attributes found to use in a filter");
                 return;
             }
 
         if (trackFilterPane == null) {
-
-            trackFilterPane = new TrackFilterPane(uniqueAttributeKeys, "Show tracks whose attribute", trackFilter);
+            trackFilterPane = new TrackFilterPane(uniqueAttributeKeys, "Show tracks whose attribute",
+                    mainFrame.getSession().getFilter());
 
         } else {
 
@@ -111,13 +102,11 @@ public class FilterTracksMenuAction extends MenuAction {
         }
 
         trackFilterPane.clearTracks();
-        trackFilterPane.addTracks(TrackManager.getInstance().getAllTracks(false));
+        trackFilterPane.addTracks(IGVMainFrame.getInstance().getTrackManager().getAllTracks(false));
 
         while (true) {
 
-            Integer response =
-                    createFilterTrackDialog(mainFrame, trackFilterPane,
-                            "Filter Tracks");
+            Integer response = createFilterTrackDialog(mainFrame, trackFilterPane, "Filter Tracks");
 
             if (response == null) {
                 continue;
@@ -126,8 +115,7 @@ public class FilterTracksMenuAction extends MenuAction {
             if (response.intValue() == JOptionPane.CANCEL_OPTION) {
 
                 // Restore previous filter state
-                boolean disableFilterState =
-                        showAllTracksFilterCheckBox.isSelected();
+                boolean disableFilterState = showAllTracksFilterCheckBox.isSelected();
                 if (disableFilterState != previousDisableFilterState) {
                     showAllTracksFilterCheckBox.setSelected(previousDisableFilterState);
                 }
@@ -143,7 +131,7 @@ public class FilterTracksMenuAction extends MenuAction {
                 return;
             } else if (response.intValue() == JOptionPane.OK_OPTION) {
 
-                configureFilterStateOfTracks(trackFilterPane);
+                filterTracks(trackFilterPane);
                 break;
             }
         }
@@ -158,10 +146,8 @@ public class FilterTracksMenuAction extends MenuAction {
                                             String title) {
 
         JScrollPane scrollPane = new JScrollPane(trackFilterPane);
-        scrollPane.setVerticalScrollBarPolicy(
-                JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
-        scrollPane.setHorizontalScrollBarPolicy(
-                JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
+        scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
 
         int optionType = JOptionPane.OK_CANCEL_OPTION;
         int messageType = JOptionPane.PLAIN_MESSAGE;
@@ -216,13 +202,12 @@ public class FilterTracksMenuAction extends MenuAction {
         panel.add(filterHeaderPanel, BorderLayout.NORTH);
         panel.add(scrollPane, BorderLayout.CENTER);
 
-        final JOptionPane optionPane =
-                new JOptionPane(panel, messageType, optionType);
-
-        optionPane.setPreferredSize(new Dimension(600, 400));
+        final JOptionPane optionPane = new JOptionPane(panel, messageType, optionType);
+        optionPane.setPreferredSize(new Dimension(700, 500));
         optionPane.setOpaque(true);
         optionPane.setBackground(Color.WHITE);
-        optionPane.addPropertyChangeListener(JOptionPane.VALUE_PROPERTY,
+        optionPane.addPropertyChangeListener(
+                JOptionPane.VALUE_PROPERTY,
                 new PropertyChangeListener() {
 
                     public void propertyChange(PropertyChangeEvent e) {
@@ -273,36 +258,34 @@ public class FilterTracksMenuAction extends MenuAction {
                         "Some of the filter values are missing." +
                                 "\nPlease enter all value before pressing ok.");
 
-                selectedValue =
-                        null;
+                selectedValue = null;
             }
-
         }
-
         return ((Integer) selectedValue);
     }
 
-    private void configureFilterStateOfTracks(TrackFilterPane trackFilterPane) {
+    private void filterTracks(TrackFilterPane trackFilterPane) {
 
         boolean showAllTracks = showAllTracksFilterCheckBox.isSelected();
         if (showAllTracks) {
 
-            List<Track> tracks = TrackManager.getInstance().getAllTracks(false);
+            List<Track> tracks = IGVMainFrame.getInstance().getTrackManager().getAllTracks(false);
             for (Track track : tracks) {
                 track.setVisible(showAllTracks);
             }
 
         } else {
-            trackFilter = trackFilterPane.getFilter();
+            TrackFilter filter = trackFilterPane.getFilter();
+            IGVMainFrame.getInstance().getSession().setFilter(filter);
             // Evaluate the filter elements
-            trackFilter.evaluate();
+            filter.evaluate();
         }
 
     }
 
     public void resetTrackFilter() {
         trackFilterPane = null;
-        trackFilter = null;
+        IGVMainFrame.getInstance().getSession().setFilter(null);
         setFilterShowAllTracks(false);
     }
 
@@ -312,50 +295,32 @@ public class FilterTracksMenuAction extends MenuAction {
         }
     }
 
-    public boolean isFilterShowAllTracks() {
-        if (showAllTracksFilterCheckBox != null) {
-            return showAllTracksFilterCheckBox.isSelected();
-        }
-        return false;
-    }
-
     public JCheckBox getShowAllTracksFilterCheckBox() {
         return showAllTracksFilterCheckBox;
     }
 
-    public TrackFilter getTrackFilter() {
+    public void updateTrackFilter() {
 
-        TrackFilter filter = null;
-        if (trackFilterPane != null) {
-            filter = trackFilterPane.getFilter();
-        }
-        return filter;
-    }
-
-    public void setTrackFilter(TrackFilter trackFilter) {
+        TrackFilter trackFilter = IGVMainFrame.getInstance().getSession().getFilter();
 
         if (trackFilter == null) {
             return;
         }
 
-        List<String> uniqueAttributeKeys =
-                AttributeManager.getInstance().getAttributeKeys();
+        List<String> uniqueAttributeKeys = AttributeManager.getInstance().getAttributeKeys();
 
 
         // If we have no attribute we can't display the
         // track filter dialog so say so and return
-        if (uniqueAttributeKeys == null ||
-                uniqueAttributeKeys.isEmpty()) {
+        if (uniqueAttributeKeys == null || uniqueAttributeKeys.isEmpty()) {
 
-            JOptionPane.showMessageDialog(mainFrame,
-                    "No attributes found to use in a filter");
+            MessageUtils.showMessage("No attributes found to use in a filter");
             return;
         }
 
         trackFilterPane = new TrackFilterPane(uniqueAttributeKeys, "Show tracks whose attribute", trackFilter);
-
         trackFilterPane.clearTracks();
-        trackFilterPane.addTracks(TrackManager.getInstance().getAllTracks(false));
+        trackFilterPane.addTracks(IGVMainFrame.getInstance().getTrackManager().getAllTracks(false));
 
         // Evaluate the filter elements
         trackFilter.evaluate();
diff --git a/src/org/broad/igv/ui/action/FitDataToWindowMenuAction.java b/src/org/broad/igv/ui/action/FitDataToWindowMenuAction.java
index f4aa870..538d5fb 100644
--- a/src/org/broad/igv/ui/action/FitDataToWindowMenuAction.java
+++ b/src/org/broad/igv/ui/action/FitDataToWindowMenuAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,14 +22,15 @@
  */
 package org.broad.igv.ui.action;
 
-import com.jidesoft.utils.SwingWorker;
 import org.apache.log4j.Logger;
 import org.broad.igv.track.Track;
 import org.broad.igv.track.TrackGroup;
 import org.broad.igv.ui.IGVMainFrame;
 import org.broad.igv.ui.UIConstants;
 import org.broad.igv.ui.panel.DataPanel;
-import org.broad.igv.ui.panel.TrackSetScrollPane;
+import org.broad.igv.ui.panel.TrackPanelScrollPane;
+import org.broad.igv.util.LongRunningTask;
+import org.broad.igv.util.NamedRunnable;
 
 import java.awt.event.ActionEvent;
 import java.util.Collection;
@@ -57,8 +58,22 @@ public class FitDataToWindowMenuAction extends MenuAction {
      */
     public void actionPerformed(ActionEvent e) {
 
-        Worker worker = new Worker();
-        worker.execute();
+        NamedRunnable runnable = new NamedRunnable() {
+            public void run() {
+                for (TrackPanelScrollPane sp : IGVMainFrame.getInstance().getTrackManager().getTrackPanelScrollPanes()) {
+                    fitTracksToPanel(sp.getDataPanel());
+                    sp.getDataPanel().doResize();
+                }
+                mainFrame.doRefresh();
+            }
+
+
+            public String getName() {
+                return "Fit data to window";
+            }
+        };
+
+        LongRunningTask.submit(runnable);
     }
 
     /**
@@ -90,42 +105,32 @@ public class FitDataToWindowMenuAction extends MenuAction {
             }
         }
 
+
         // Auto resize the height of the visible tracks
         if (visibleTrackCount > 0) {
-
-            int adjustedVisibleHeight = visibleHeight;
+            int groupGapHeight = (groups.size() + 1) * UIConstants.groupGap;
+            int adjustedVisibleHeight = visibleHeight - groupGapHeight;
 
             if (adjustedVisibleHeight > 0) {
 
-                int newTrackHeight = adjustedVisibleHeight / visibleTrackCount;
+                float delta = (float) adjustedVisibleHeight / visibleTrackCount;
 
                 // If the new track height is less than 1 theres nothing we 
                 // can do to force all tracks to fit so we do nothing
-                if (newTrackHeight < 1) {
-                    newTrackHeight = 1;
+                if (delta < 1) {
+                    delta = 1;
                 }
 
+                int iTotal = 0;
+                float target = 0;
                 for (TrackGroup group : groups) {
                     List<Track> tracks = group.getTracks();
                     for (Track track : tracks) {
+                        target += delta;
+                        int newHeight = (int) Math.round(target - iTotal);
+                        iTotal += newHeight;
                         if (track.isVisible()) {
-                            track.setHeight(newTrackHeight);
-                        }
-                    }
-                }
-
-                // Extra space to fill in
-                int extraPixels = adjustedVisibleHeight % visibleTrackCount;
-
-                for (TrackGroup group : groups) {
-                    List<Track> tracks = group.getTracks();
-                    for (Track track : tracks) {
-                        if (extraPixels == 0) {
-                            break;
-                        }
-                        if (track.isVisible()) {
-                            track.setHeight(newTrackHeight + 1);
-                            --extraPixels;
+                            track.setHeight(newHeight);
                         }
                     }
                 }
@@ -135,41 +140,4 @@ public class FitDataToWindowMenuAction extends MenuAction {
         return success;
     }
 
-    /**
-     * The worker class for this action.
-     *
-     * @param String
-     * @param Void
-     */
-    class Worker<String, Void> extends SwingWorker {
-
-        boolean success;
-
-        @Override
-        /**
-         * This method is executed in a background thread (NOT the swing
-         * thread).
-         */
-        protected Object doInBackground() throws Exception {
-            // Process Data and Feature Tracks
-            success = false;
-            for (TrackSetScrollPane sp : IGVMainFrame.getInstance().getTrackSetScrollPanes()) {
-                success = success || fitTracksToPanel(sp.getDataPanel());
-            }
-
-            return success;
-
-        }
-
-        @Override
-        /**
-         * This method is executed in the swing thread when doInBackground 
-         * completes.
-         */
-        protected void done() {
-            mainFrame.clearImageCacheWithNoRepaint();
-            mainFrame.updateTrackState();
-
-        }
-    }
 }
diff --git a/src/org/broad/igv/ui/action/GroupTracksMenuAction.java b/src/org/broad/igv/ui/action/GroupTracksMenuAction.java
index 4d5c497..91e2acb 100644
--- a/src/org/broad/igv/ui/action/GroupTracksMenuAction.java
+++ b/src/org/broad/igv/ui/action/GroupTracksMenuAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,11 +23,11 @@
 package org.broad.igv.ui.action;
 
 import org.broad.igv.track.AttributeManager;
-import org.broad.igv.track.TrackManager;
 import org.broad.igv.ui.AttributeSelectionDialog;
-import org.broad.igv.ui.GuiUtilities;
 import org.broad.igv.ui.IGVMainFrame;
 import org.broad.igv.ui.UIConstants;
+import org.broad.igv.ui.WaitCursorManager;
+import org.broad.igv.ui.util.UIUtilities;
 
 import javax.swing.*;
 import java.awt.event.ActionEvent;
@@ -52,7 +52,7 @@ public class GroupTracksMenuAction extends MenuAction {
     @Override
     public void actionPerformed(ActionEvent e) {
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
                 doGroupBy();
@@ -65,12 +65,16 @@ public class GroupTracksMenuAction extends MenuAction {
 
         final AttributeSelectionDialog dlg = new AttributeSelectionDialog(mainFrame, true);
 
-        List<String> attributeKeys =
-                AttributeManager.getInstance().getAttributeKeys();
-        if (attributeKeys != null) {
-            Collections.sort(attributeKeys,
-                    AttributeManager.getInstance().getAttributeComparator());
-        }
+        List<String> attributeKeys = AttributeManager.getInstance().getAttributeKeys();
+
+
+        // Sorting disabled -- order will match the order in the panel.  If sorting is desired make a copy
+        // of the array so the panel is not affected.
+
+        //if (attributeKeys != null) {
+        //    Collections.sort(attributeKeys,
+        //            AttributeManager.getInstance().getAttributeComparator());
+        //}
 
         ArrayList<String> selections = new ArrayList(attributeKeys);
 
@@ -80,7 +84,7 @@ public class GroupTracksMenuAction extends MenuAction {
         dlg.setModel(new javax.swing.DefaultComboBoxModel(selArray));
         dlg.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
 
-        String currentSelection = TrackManager.getInstance().getGroupByAttribute();
+        String currentSelection = IGVMainFrame.getInstance().getTrackManager().getGroupByAttribute();
         if (currentSelection == null) {
             dlg.setSelectedIndex(0);
         } else {
@@ -91,15 +95,14 @@ public class GroupTracksMenuAction extends MenuAction {
 
         if (!dlg.isCanceled()) {
 
+
             int selIndex = dlg.getSelectedIndex();
             String selectedAttribute = (selIndex == 0 ? null : selArray[selIndex]);
-            TrackManager.getInstance().setGroupByAttribute(selectedAttribute);
+            System.out.println("Enter group tracks");
+            IGVMainFrame.getInstance().getTrackManager().setGroupByAttribute(selectedAttribute);
+            System.out.println("Exit group tracks");
             mainFrame.updateTrackState();
-            mainFrame.doRefresh();
-            mainFrame.fireViewChangedEvent();
 
-            //boolean isEnabled = PreferenceManager.getInstance().isImageCachingEnabled();
-            //mainFrame.setDisableImageCaching(isEnabled);
         }
 
     }
diff --git a/src/org/broad/igv/ui/action/ImportRegionsMenuAction.java b/src/org/broad/igv/ui/action/ImportRegionsMenuAction.java
index 69408ed..4fb58aa 100644
--- a/src/org/broad/igv/ui/action/ImportRegionsMenuAction.java
+++ b/src/org/broad/igv/ui/action/ImportRegionsMenuAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,9 +23,9 @@
 package org.broad.igv.ui.action;
 
 import org.apache.log4j.Logger;
-import org.broad.igv.ui.GuiUtilities;
 import org.broad.igv.ui.IGVMainFrame;
 import org.broad.igv.ui.UIConstants;
+import org.broad.igv.ui.util.UIUtilities;
 
 import java.awt.event.ActionEvent;
 
@@ -45,7 +45,7 @@ public class ImportRegionsMenuAction extends RegionsBaseMenuAction {
     @Override
     public void actionPerformed(ActionEvent e) {
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
                 importExportRegionsOfInterest(Direction.IMPORT);
diff --git a/src/org/broad/igv/ui/action/LoadFilesMenuAction.java b/src/org/broad/igv/ui/action/LoadFilesMenuAction.java
index 31668dd..199f892 100644
--- a/src/org/broad/igv/ui/action/LoadFilesMenuAction.java
+++ b/src/org/broad/igv/ui/action/LoadFilesMenuAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,12 +23,13 @@
 package org.broad.igv.ui.action;
 
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
+import org.broad.igv.Globals;
 import org.broad.igv.PreferenceManager;
-import org.broad.igv.ui.GuiUtilities;
 import org.broad.igv.ui.IGVMainFrame;
 import org.broad.igv.ui.UIConstants;
 import org.broad.igv.ui.util.FileChooserDialog;
+import org.broad.igv.ui.util.MessageUtils;
+import org.broad.igv.ui.util.UIUtilities;
 import org.broad.igv.util.ResourceLocator;
 
 import javax.swing.*;
@@ -54,7 +55,7 @@ public class LoadFilesMenuAction extends MenuAction {
     @Override
     public void actionPerformed(ActionEvent e) {
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
                 loadFiles(chooseTrackFiles());
@@ -118,8 +119,8 @@ public class LoadFilesMenuAction extends MenuAction {
                 } else {
 
                     String path = file.getAbsolutePath();
-                    if (path.endsWith(IGVConstants.SESSION_FILE_EXTENSION)) {
-                        org.broad.igv.ui.util.UIUtilities.showAndLogErrorMessage(mainFrame,
+                    if (path.endsWith(Globals.SESSION_FILE_EXTENSION)) {
+                        MessageUtils.showAndLogErrorMessage(mainFrame,
                                 "File " + path +
                                         " appears to be an IGV Session file - " +
                                         "please use the Open Session menu item " +
@@ -133,7 +134,7 @@ public class LoadFilesMenuAction extends MenuAction {
             files = validFileList.toArray(new File[validFileList.size()]);
 
             if (!allFilesExist) {
-                org.broad.igv.ui.util.UIUtilities.showAndLogErrorMessage(mainFrame, buffer.toString(), log);
+                MessageUtils.showAndLogErrorMessage(mainFrame, buffer.toString(), log);
             }
 
             if (files.length > 0) {
diff --git a/src/org/broad/igv/ui/action/LoadFromScriptMenuAction.java b/src/org/broad/igv/ui/action/LoadFromScriptMenuAction.java
new file mode 100644
index 0000000..56aa316
--- /dev/null
+++ b/src/org/broad/igv/ui/action/LoadFromScriptMenuAction.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.ui.action;
+
+import com.jidesoft.utils.SwingWorker;
+import org.apache.log4j.Logger;
+import org.broad.igv.PreferenceManager;
+import org.broad.igv.main.BatchRunner;
+import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.UIConstants;
+import org.broad.igv.ui.util.FileChooserDialog;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+import java.io.File;
+
+
+public class LoadFromScriptMenuAction extends MenuAction {
+
+    static Logger log = Logger.getLogger(LoadFilesMenuAction.class);
+    IGVMainFrame mainFrame;
+
+    public LoadFromScriptMenuAction(String label, int mnemonic, IGVMainFrame mainFrame) {
+        super(label, null, mnemonic);
+        this.mainFrame = mainFrame;
+        setToolTipText(UIConstants.LOAD_TRACKS_TOOLTIP);
+    }
+
+    public void actionPerformed(ActionEvent e) {
+
+        if (e.getActionCommand().equalsIgnoreCase("run batch script...")) {
+            File script = chooseScriptFile();
+            if (script != null && script.isFile()) {
+                final BatchRunner bRun = new BatchRunner(script.getPath());
+
+                SwingWorker worker = new SwingWorker() {
+
+                    @Override
+                    protected Object doInBackground() throws Exception {
+                        bRun.run();
+                        return null;
+                    }
+                };
+
+                worker.execute();
+
+            }
+        }
+    }
+
+
+    private File chooseScriptFile() {
+
+        File lastDirectoryFile =
+                PreferenceManager.getInstance().getLastTrackDirectory();
+        File scriptFile = null;
+
+        // Get Track Files
+        FileChooserDialog trackFileDialog = mainFrame.getTrackFileChooser();
+        trackFileDialog.setLocationRelativeTo(mainFrame);
+        trackFileDialog.setTitle("Select Script");
+        trackFileDialog.setMultiSelectionEnabled(true);
+        trackFileDialog.setSelectedFile(null);
+        trackFileDialog.setCurrentDirectory(lastDirectoryFile);
+        trackFileDialog.setFileSelectionMode(JFileChooser.FILES_ONLY);
+
+        trackFileDialog.setVisible(true);
+
+        if (!trackFileDialog.isCanceled()) {
+
+            File lastFile = trackFileDialog.getSelectedFile();
+            if (lastFile != null && !lastFile.isDirectory()) {
+                lastFile = lastFile.getParentFile();
+
+                // Store the last accessed file location
+                PreferenceManager.getInstance().setLastTrackDirectory(lastFile);
+            }
+
+            scriptFile = trackFileDialog.getSelectedFile();
+            trackFileDialog.setSelectedFile(null);
+        }
+
+        mainFrame.resetStatusMessage();
+        return scriptFile;
+    }
+}
diff --git a/src/org/broad/igv/ui/action/LoadFromServerAction.java b/src/org/broad/igv/ui/action/LoadFromServerAction.java
index 8221c73..bacbebd 100644
--- a/src/org/broad/igv/ui/action/LoadFromServerAction.java
+++ b/src/org/broad/igv/ui/action/LoadFromServerAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -25,13 +25,17 @@ package org.broad.igv.ui.action;
 
 import org.apache.log4j.Logger;
 import org.broad.igv.PreferenceManager;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.track.AttributeManager;
-import org.broad.igv.track.TrackManager;
-import org.broad.igv.ui.*;
-import org.broad.igv.ui.ResourceCheckBoxList.ResourceCheckBox;
-import org.broad.igv.util.ConnectionTimerTask;
+import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.ResourceTree;
+import org.broad.igv.ui.UIConstants;
+import org.broad.igv.ui.util.MessageUtils;
+import org.broad.igv.ui.util.UIUtilities;
+import org.broad.igv.util.IGVHttpUtils;
 import org.broad.igv.util.ResourceLocator;
 import org.broad.igv.util.Utilities;
+import org.broad.tribble.util.HttpUtils;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.NodeList;
@@ -40,10 +44,7 @@ import org.xml.sax.SAXParseException;
 import javax.swing.*;
 import javax.xml.parsers.DocumentBuilderFactory;
 import java.awt.event.ActionEvent;
-import java.io.BufferedReader;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStreamReader;
+import java.io.*;
 import java.net.*;
 import java.util.*;
 
@@ -55,96 +56,104 @@ public class LoadFromServerAction extends MenuAction {
     static Logger log = Logger.getLogger(LoadFromServerAction.class);
     IGVMainFrame mainFrame;
 
-    /**
-     * Constructs ...
-     *
-     * @param label
-     * @param mnemonic
-     * @param mainFrame
-     */
+    // Keep track of authorization failures so user isn't constantly harranged
+    static HashSet<String> failedURLs = new HashSet();
+
+
     public LoadFromServerAction(String label, int mnemonic, IGVMainFrame mainFrame) {
         super(label, null, mnemonic);
         this.mainFrame = mainFrame;
         setToolTipText(UIConstants.LOAD_SERVER_DATA_TOOLTIP);
     }
 
-    /**
-     * Method description
-     *
-     * @param evt
-     */
+
     @Override
     public void actionPerformed(ActionEvent evt) {
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        mainFrame.setStatusBarMessage("Loading ...");
 
-            public void run() {
-                try {
-                    mainFrame.setStatusBarMessage("Loading ...");
+        String urlString = PreferenceManager.getInstance().getDataServerURL();
+        String genomeId = ViewContext.getInstance().getGenomeId();
+        String genomeURL = urlString.replaceAll("\\$\\$", genomeId);
+        try {
 
-                    try {
 
-                        String urlString = PreferenceManager.getInstance().getDataServerURL();
-                        String genomeId = IGVModel.getInstance().getViewContext().getGenomeId();
-                        String genomeURL = urlString.replaceAll("\\$\\$", genomeId);
+            // Add genome parameter for users with hardcoded reference to registry servlet
+            if (genomeURL.startsWith("http:") || genomeURL.startsWith("https:"))
+                genomeURL += "?genome=" + genomeId;
+
+
+            InputStream is = null;
+            HttpURLConnection conn = null;
+            LinkedHashSet<URL> urls = null;
+            try {
+                URL masterResourceFileURL = new URL(genomeURL);
+                if (genomeURL.startsWith("ftp:")) {
+                    MessageUtils.showMessage("FTP protocol not supported for data registry URL");
+                    return;
+                } else if (genomeURL.startsWith("http:") || genomeURL.startsWith("https:")) {
+                    conn = IGVHttpUtils.openConnection(masterResourceFileURL);
+                    conn.setReadTimeout(10000);
+                    conn.setConnectTimeout(10000);
+                    is = IGVHttpUtils.openHttpStream(masterResourceFileURL, conn);
+                } else {
+                    File file = new File(genomeURL.startsWith("file:") ? masterResourceFileURL.getFile() : genomeURL);
+                    if (!file.exists()) {
+                        MessageUtils.showMessage("ERROR: File does not exist: " + file.getAbsolutePath());
+                        return;
+                    }
+                    is = new FileInputStream(file);
+                }
 
-                        // Add genome parameter for users with hardcoded references to registry servlet
-                        genomeURL += "?genome=" + genomeId;
+                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
+                urls = getResourceUrls(bufferedReader);
+            } catch (IOException e) {
 
-                        URL masterResourceFileURL = new URL(genomeURL);
+                if (conn.getResponseCode() == 401) {
+                    String msg = "Authorization failure accessing resource: " + genomeURL;
+                    MessageUtils.showMessage(msg);
+                    log.error("Error accessing URL: " + genomeURL, e);
+                }
+            }
+            finally {
+                if (is != null) is.close();
+                if (conn != null) conn.disconnect();
+            }
 
-                        URLConnection conn = masterResourceFileURL.openConnection();
-                        conn.setReadTimeout(10000);
-                        conn.setConnectTimeout(10000);
+            if (urls == null || urls.isEmpty()) {
+                JOptionPane.showMessageDialog(mainFrame,
+                        "No datasets are available for the current genome (" + genomeId + ").");
+            } else {
 
-                        LinkedHashSet<URL> urls = null;
-                        try {
-                            BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(conn.getInputStream()));
-                            urls = getResourceUrls(bufferedReader);
-                        } catch (IOException e) {
-                            log.error("Error accessing URL: " + genomeURL, e);
-                        }
+                List<ResourceLocator> locators = selectResources(urls);
+                if (locators != null) {
+                    mainFrame.loadTracks(locators);
+                }
+            }
 
 
-                        if (urls == null || urls.isEmpty()) {
-                            JOptionPane.showMessageDialog(mainFrame,
-                                    "No datasets are available for the current genome (" +
-                                            genomeId + ").");
-                        } else {
+        } catch (UnknownHostException e) {
+            String msg = "The application could not contact the server: Unknown Host Failure\nHost: " + e.getMessage();
+            log.error(msg);
+            MessageUtils.showMessage(msg);
 
-                            List<ResourceLocator> locators = selectResources(urls);
-                            if (locators != null) {
-                                mainFrame.loadTracks(locators);
-                            }
-                        }
+        } catch (ConnectException e) {
+            String msg = "The application could not contact the server: Connection Failure!\n" + e.getMessage();
+            log.error(msg);
+            MessageUtils.showMessage(msg);
+        }
 
+        catch (Exception e) {
+            String msg = "Could not load information from server!\n" + e.getMessage();
+            log.error(msg);
+            MessageUtils.showMessage(msg);
 
-                    } catch (UnknownHostException e) {
-                        org.broad.igv.ui.util.UIUtilities.showErrorMessage(
-                                mainFrame,
-                                "The application could not contact the server: Unknown Host Failure!\nHost: " + e.getMessage());
-                    } catch (ConnectException e) {
-                        org.broad.igv.ui.util.UIUtilities.showErrorMessage(
-                                mainFrame,
-                                "The application could not contact the server: Connection Failure!\n" + e.getMessage());
-                    } catch (Exception e) {
-                        org.broad.igv.ui.util.UIUtilities.showErrorMessage(mainFrame,
-                                "Could not load information from server!\n" + e.getMessage());
-                    }
-                } finally {
-                    mainFrame.showLoadedTrackCount();
-                }
+        } finally {
+            mainFrame.showLoadedTrackCount();
+        }
 
-            }
-        });
     }
 
-    /**
-     * Method description
-     *
-     * @param xmlUrls
-     * @return
-     */
     public List<ResourceLocator> selectResources(final LinkedHashSet<URL> xmlUrls) {
 
         if ((xmlUrls == null) || xmlUrls.isEmpty()) {
@@ -156,11 +165,9 @@ public class LoadFromServerAction extends MenuAction {
         boolean xmlParsingError = false;
         try {
 
-            buffer.append(
-                    "<html>The following urls could not be processed due to load failures:<br>");
+            buffer.append("<html>The following urls could not be processed due to load failures:<br>");
 
-            Document masterDocument =
-                    DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+            Document masterDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
 
             Element rootNode = masterDocument.createElement("Global");
             rootNode.setAttribute("name", "Available Datasets");
@@ -171,14 +178,21 @@ public class LoadFromServerAction extends MenuAction {
             // Merge all documents into one xml document for processing
             for (URL url : xmlUrls) {
 
-                try {
+                // Skip urls that have previously failed due to authorization
+                if (failedURLs.contains(url.toString())) {
+                    continue;
+                }
 
+                try {
+                    HttpURLConnection connection = null;
+                    InputStream is = null;
                     Document xmlDocument = null;
                     try {
-                        URLConnection connection = url.openConnection();
+                        connection = IGVHttpUtils.openConnection(url);
                         connection.setConnectTimeout(10000);
                         connection.setReadTimeout(10000);
-                        xmlDocument = Utilities.createDOMDocumentFromXmlStream(connection.getInputStream());
+                        is = IGVHttpUtils.openHttpStream(url, connection);
+                        xmlDocument = Utilities.createDOMDocumentFromXmlStream(is);
                     }
                     catch (java.net.SocketTimeoutException e) {
                         xmlParsingError = true;
@@ -210,50 +224,77 @@ public class LoadFromServerAction extends MenuAction {
                         buffer.append("]\n");
                         continue;
                     }
+                    catch (IOException e) {
+                        String msg = "";
+                        if (connection.getResponseCode() == 401) {
+                            failedURLs.add(url.toString());
+                            msg = "Authorization failure accessing resource: " + url;
+                        } else {
+                            msg = "Error accessing dataset list: " + e.toString();
+                        }
+                        MessageUtils.showMessage(msg);
+                        log.error("Error accessing URL: " + url, e);
+                    }
+                    finally {
+                        if (is != null) is.close();
+                        if (connection != null) connection.disconnect();
+                    }
 
-                    NodeList elements = xmlDocument.getElementsByTagName("Global");
-                    Element global = (Element) elements.item(0);
-                    NodeList nodes = global.getChildNodes();
-                    Element categoryNode = masterDocument.createElement("Category");
-                    categoryNode.setAttribute("name", global.getAttribute("name"));
-                    categoryNode.setAttribute("hyperlink", global.getAttribute("hyperlink"));
-                    rootNode.appendChild(categoryNode);
-                    int size = nodes.getLength();
-                    for (int i = 0; i < size; i++) {
-                        categoryNode.appendChild(masterDocument.importNode(nodes.item(i), true));
+                    if (xmlDocument != null) {
+                        NodeList elements = xmlDocument.getElementsByTagName("Global");
+                        Element global = (Element) elements.item(0);
+                        NodeList nodes = global.getChildNodes();
+                        Element categoryNode = masterDocument.createElement("Category");
+                        categoryNode.setAttribute("name", global.getAttribute("name"));
+                        categoryNode.setAttribute("hyperlink", global.getAttribute("hyperlink"));
+                        rootNode.appendChild(categoryNode);
+                        int size = nodes.getLength();
+                        for (int i = 0; i < size; i++) {
+                            categoryNode.appendChild(masterDocument.importNode(nodes.item(i), true));
+                        }
                     }
                 } catch (Exception e) {
                     String message = "Cannot create an XML Document from " + url.toString();
                     log.error(message, e);
                     continue;
                 }
+
             }
             if (xmlParsingError) {
                 JOptionPane.showMessageDialog(mainFrame, buffer.toString());
             }
 
+
             /**
              * Resource Tree
              */
             HashSet<ResourceLocator> selectedLocators =
-                    ResourceTree.getInstance().showResourceTreeDialog(mainFrame, masterDocument,
-                            "Available Datasets");
+                    ResourceTree.getInstance().showResourceTreeDialog(mainFrame, masterDocument, "Available Datasets");
 
             List<ResourceLocator> newLoadList = new ArrayList();
 
-            Set<ResourceLocator> loadedResources = TrackManager.getInstance().getDataResourceLocators();
+            Set<ResourceLocator> loadedResources = IGVMainFrame.getInstance().getTrackManager().getDataResourceLocators();
             loadedResources.addAll(AttributeManager.getInstance().getLoadedResources());
 
-            for (ResourceLocator locator : selectedLocators) {
+            if (selectedLocators != null) {
+                for (ResourceLocator locator : selectedLocators) {
 
-                // Don't reload data that is already loaded
-                if (loadedResources.contains(locator)) {
-                    continue;
-                }
+                    // skip locators with no data path
+                    if(locator.getPath() == null) {
+                        continue;
+                    }
 
-                newLoadList.add(locator);
+                    // Don't reload data that is already loaded
+                    if (loadedResources.contains(locator)) {
+                        continue;
+                    }
+
+                    newLoadList.add(locator);
+                }
             }
+
             return newLoadList;
+
         } catch (Exception e) {
             log.error("Could not load information from server", e);
             return null;
@@ -272,8 +313,10 @@ public class LoadFromServerAction extends MenuAction {
      * @throws java.net.MalformedURLException
      * @throws java.io.IOException
      */
-    private LinkedHashSet<URL> getResourceUrls(BufferedReader bufferedReader)
-            throws MalformedURLException, IOException {
+    private LinkedHashSet<URL> getResourceUrls
+            (BufferedReader
+                    bufferedReader)
+            throws IOException {
 
         LinkedHashSet<URL> xmlFileUrls = new LinkedHashSet();
         while (true) {
@@ -288,42 +331,5 @@ public class LoadFromServerAction extends MenuAction {
         return xmlFileUrls;
     }
 
-    /**
-     * Popups a dialog from which the user can select one or more XML resource
-     * files.  Not currelent used.
-     *
-     * @param bufferedReader
-     * @return
-     * @throws java.net.MalformedURLException
-     * @throws java.io.IOException
-     */
-    private Set<URL> getResourceUrlsFromDialog(BufferedReader bufferedReader)
-            throws MalformedURLException, IOException {
-
-        List<ResourceCheckBox> xmlFileUrls = new ArrayList();
-
-        while (true) {
-            String xmlFileUrl = bufferedReader.readLine();
-            if (xmlFileUrl == null) {
-                break;
-            }
-            xmlFileUrl = xmlFileUrl.trim();
-
-            ResourceCheckBox checkbox = new ResourceCheckBox(xmlFileUrl, new URL(xmlFileUrl));
-            xmlFileUrls.add(checkbox);
-        }
-
-        Object[] data = xmlFileUrls.toArray();
-        ResourceCheckBoxList list = new ResourceCheckBoxList(data);
 
-        int status = JOptionPane.showConfirmDialog(mainFrame, new JScrollPane(list),
-                "Resource File List", JOptionPane.OK_CANCEL_OPTION,
-                JOptionPane.PLAIN_MESSAGE, null);
-
-        if ((status == JOptionPane.CANCEL_OPTION) || (status == JOptionPane.CLOSED_OPTION)) {
-            return new HashSet<URL>();
-        }
-
-        return list.getSelectedURLs();
-    }
 }
diff --git a/src/org/broad/igv/ui/action/LoadFromURLMenuAction.java b/src/org/broad/igv/ui/action/LoadFromURLMenuAction.java
index 5e793e2..116f58f 100644
--- a/src/org/broad/igv/ui/action/LoadFromURLMenuAction.java
+++ b/src/org/broad/igv/ui/action/LoadFromURLMenuAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -25,10 +25,13 @@ package org.broad.igv.ui.action;
 import org.apache.log4j.Logger;
 import org.broad.igv.ui.IGVMainFrame;
 import org.broad.igv.ui.UIConstants;
+import org.broad.igv.ui.util.MessageUtils;
 import org.broad.igv.util.ResourceLocator;
 
 import javax.swing.*;
 import java.awt.event.ActionEvent;
+import java.net.MalformedURLException;
+import java.net.URL;
 import java.util.Arrays;
 
 /**
@@ -37,7 +40,9 @@ import java.util.Arrays;
 public class LoadFromURLMenuAction extends MenuAction {
 
     static Logger log = Logger.getLogger(LoadFilesMenuAction.class);
-    IGVMainFrame mainFrame;
+    public static final String LOAD_FROM_DAS = "Load from DAS...";
+    public static final String LOAD_FROM_URL = "Load from URL...";
+    private IGVMainFrame mainFrame;
 
     public LoadFromURLMenuAction(String label, int mnemonic, IGVMainFrame mainFrame) {
         super(label, null, mnemonic);
@@ -48,20 +53,52 @@ public class LoadFromURLMenuAction extends MenuAction {
     @Override
     public void actionPerformed(ActionEvent e) {
 
-        if (e.getActionCommand().equalsIgnoreCase("load from URL...")){
-            String url = JOptionPane.showInputDialog(IGVMainFrame.getInstance(), "Enter Server URL ");
+        if (e.getActionCommand().equalsIgnoreCase(LOAD_FROM_URL)) {
+            String url = JOptionPane.showInputDialog(IGVMainFrame.getInstance(), "Enter Http URL ");
             if (url != null && url.trim().length() > 0) {
-                ResourceLocator rl = new ResourceLocator(url.trim());
-                mainFrame.loadTracks(Arrays.asList(rl));
+                if (url.endsWith(".xml")) {
+                   loadSession(url);
+                } else {
+                    ResourceLocator rl = new ResourceLocator(url.trim());
+                    mainFrame.loadTracks(Arrays.asList(rl));
+
+                }
             }
-        }else if((e.getActionCommand().equalsIgnoreCase("connect to das server..."))){
+        } else if ((e.getActionCommand().equalsIgnoreCase(LOAD_FROM_DAS))) {
             String url = JOptionPane.showInputDialog(IGVMainFrame.getInstance(), "Enter DAS URL ");
             if (url != null && url.trim().length() > 0) {
                 ResourceLocator rl = new ResourceLocator(url.trim());
-                rl.setType("DAS");
+                rl.setType("das");
                 mainFrame.loadTracks(Arrays.asList(rl));
             }
         }
     }
+
+    private void loadSession(String url) {
+        // If anything has been loaded warn the users.  Popping up the
+        // warning all the time will get annoying.
+        if (IGVMainFrame.getInstance().getTrackManager().getAllTracks(false).size() > 0) {
+            int status =
+                    JOptionPane.showConfirmDialog(
+                            mainFrame,
+                            UIConstants.OVERWRITE_SESSION_MESSAGE,
+                            null,
+                            JOptionPane.OK_CANCEL_OPTION,
+                            JOptionPane.PLAIN_MESSAGE,
+                            null);
+
+            if (status == JOptionPane.CANCEL_OPTION ||
+                    status == JOptionPane.CLOSED_OPTION) {
+                return;
+            }
+        }
+
+        try {
+            mainFrame.doRestoreSession(new URL(url), null);
+        } catch (MalformedURLException e1) {
+            MessageUtils.showMessage("Error loading url: " + url + " (" + e1.toString() + ")");
+        }
+
+    }
 }
 
diff --git a/src/org/broad/igv/ui/action/MenuAction.java b/src/org/broad/igv/ui/action/MenuAction.java
index 02a5dd1..720fdfe 100644
--- a/src/org/broad/igv/ui/action/MenuAction.java
+++ b/src/org/broad/igv/ui/action/MenuAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/action/NewSessionMenuAction.java b/src/org/broad/igv/ui/action/NewSessionMenuAction.java
index eca8d13..dacf1c8 100644
--- a/src/org/broad/igv/ui/action/NewSessionMenuAction.java
+++ b/src/org/broad/igv/ui/action/NewSessionMenuAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,13 +22,10 @@
  */
 package org.broad.igv.ui.action;
 
-import org.broad.igv.IGVConstants;
-import org.broad.igv.track.TrackManager;
-import org.broad.igv.ui.GuiUtilities;
 import org.broad.igv.ui.IGVMainFrame;
 import org.broad.igv.ui.UIConstants;
+import org.broad.igv.ui.util.UIUtilities;
 
-import javax.swing.*;
 import java.awt.event.ActionEvent;
 
 /**
@@ -47,30 +44,12 @@ public class NewSessionMenuAction extends MenuAction {
     @Override
     public void actionPerformed(ActionEvent e) {
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
-                try {
-                    if (TrackManager.getInstance().getAllTracks(false).size() > 0) {
-                        /*
-                                JOptionPane.showConfirmDialog(
-                                        mainFrame,
-                                        IGVConstants.NEW_SESSION_MESSAGE,
-                                        null,
-                                        JOptionPane.OK_CANCEL_OPTION,
-                                        JOptionPane.PLAIN_MESSAGE,
-                                        null);
 
-                        if (status == JOptionPane.CANCEL_OPTION ||
-                                status == JOptionPane.CLOSED_OPTION) {
-                            return;
-                        }
-                        */
-                    }
-                    mainFrame.reset(); // Clear everything but the genome
-                    mainFrame.doRefresh();
-                } finally {
-                }
+                mainFrame.createNewSession(null); // Clear everything but the genome 
+
             }
         });
     }
diff --git a/src/org/broad/igv/ui/action/OpenSessionMenuAction.java b/src/org/broad/igv/ui/action/OpenSessionMenuAction.java
index 419526b..099af06 100644
--- a/src/org/broad/igv/ui/action/OpenSessionMenuAction.java
+++ b/src/org/broad/igv/ui/action/OpenSessionMenuAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,13 +22,13 @@
  */
 package org.broad.igv.ui.action;
 
-import org.broad.igv.IGVConstants;
+import com.jidesoft.utils.SwingWorker;
+import org.apache.log4j.Logger;
 import org.broad.igv.PreferenceManager;
-import org.broad.igv.track.TrackManager;
-import org.broad.igv.ui.GuiUtilities;
 import org.broad.igv.ui.IGVMainFrame;
 import org.broad.igv.ui.UIConstants;
 import org.broad.igv.ui.util.FileChooserDialog;
+import org.broad.igv.ui.util.UIUtilities;
 
 import javax.swing.*;
 import java.awt.event.ActionEvent;
@@ -44,15 +44,17 @@ import java.io.File;
  */
 public class OpenSessionMenuAction extends MenuAction {
 
-    IGVMainFrame mainFrame;
-
-    File sessionFile = null;
+    private static Logger log = Logger.getLogger(OpenSessionMenuAction.class);
+    private IGVMainFrame mainFrame;
+    private File sessionFile = null;
+    private boolean autoload = false;
 
     public OpenSessionMenuAction(String label, File sessionFile, IGVMainFrame mainFrame) {
         super(label);
         this.sessionFile = sessionFile;
         this.mainFrame = mainFrame;
         setToolTipText(UIConstants.RESTORE_SESSION_TOOLTIP);
+        autoload = true;
     }
 
     public OpenSessionMenuAction(String label, int mnemonic, IGVMainFrame mainFrame) {
@@ -64,23 +66,36 @@ public class OpenSessionMenuAction extends MenuAction {
     @Override
     public void actionPerformed(ActionEvent e) {
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        //dhmay adding if statement for restore of specific session specified in menu
+        if (sessionFile == null || autoload == false) {
+            FileChooserDialog dialog = new FileChooserDialog(mainFrame, true);
+            dialog.setTitle("Open Session");
+            dialog.setSelectedFile(null);
+            dialog.setFileSelectionMode(JFileChooser.FILES_ONLY);
+
+            File lastSessionDirectory =
+                    PreferenceManager.getInstance().getLastSessionDirectory();
+            dialog.setCurrentDirectory(lastSessionDirectory);
+            dialog.setVisible(true);
 
-            public void run() {
-                doRestoreSession();
+            if (dialog.isCanceled()) {
+                return;
             }
-        });
+            sessionFile = dialog.getSelectedFile();
+        }
+        doRestoreSession();
+
     }
 
     final public void doRestoreSession() {
 
         // If anything has been loaded warn the users.  Popping up the
         // warning all the time will get annoying.
-        if (TrackManager.getInstance().getAllTracks(false).size() > 0) {
+        if (IGVMainFrame.getInstance().getTrackManager().getAllTracks(false).size() > 0) {
             int status =
                     JOptionPane.showConfirmDialog(
                             mainFrame,
-                            IGVConstants.OVERWRITE_SESSION_MESSAGE,
+                            UIConstants.OVERWRITE_SESSION_MESSAGE,
                             null,
                             JOptionPane.OK_CANCEL_OPTION,
                             JOptionPane.PLAIN_MESSAGE,
@@ -93,23 +108,18 @@ public class OpenSessionMenuAction extends MenuAction {
         }
 
         if (sessionFile != null) {
-            mainFrame.doRestoreSession(sessionFile, null, true);
-        } else {
-            FileChooserDialog dialog = new FileChooserDialog(mainFrame, true);
-            dialog.setTitle("Open Session");
-            dialog.setSelectedFile(null);
-            dialog.setFileSelectionMode(JFileChooser.FILES_ONLY);
 
-            File lastSessionDirectory =
-                    PreferenceManager.getInstance().getLastSessionDirectory();
-            dialog.setCurrentDirectory(lastSessionDirectory);
-            dialog.setVisible(true);
+            SwingWorker worker = new SwingWorker() {
+                @Override
+                protected Object doInBackground() throws Exception {
+                    mainFrame.doRestoreSession(sessionFile, null);
+                    return null;
+                }
+            };
+            worker.execute();
 
-            if (dialog.isCanceled()) {
-                return;
-            }
-            mainFrame.doRestoreSession(dialog.getSelectedFile(), null, true);
         }
-
     }
+
 }
+
diff --git a/src/org/broad/igv/ui/action/RegionsBaseMenuAction.java b/src/org/broad/igv/ui/action/RegionsBaseMenuAction.java
index 061b851..7f34f65 100644
--- a/src/org/broad/igv/ui/action/RegionsBaseMenuAction.java
+++ b/src/org/broad/igv/ui/action/RegionsBaseMenuAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,12 +23,12 @@
 package org.broad.igv.ui.action;
 
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
 import org.broad.igv.PreferenceManager;
 import org.broad.igv.ui.IGVMainFrame;
-import org.broad.igv.ui.IGVModel;
 import org.broad.igv.ui.RegionOfInterest;
+import org.broad.igv.ui.UIConstants;
 import org.broad.igv.ui.util.FileChooser;
+import org.broad.igv.ui.util.MessageUtils;
 
 import javax.swing.*;
 import java.io.BufferedReader;
@@ -61,7 +61,7 @@ public class RegionsBaseMenuAction extends MenuAction {
     public final void importExportRegionsOfInterest(Direction direction) {
         File exportRegionDirectory = PreferenceManager.getInstance().getLastExportedRegionDirectory();
         if (exportRegionDirectory == null) {
-            exportRegionDirectory = new File(IGVConstants.getDefaultUserDirectory());
+            exportRegionDirectory = UIConstants.getUserDirectory();
         }
 
         FileChooser exportedRegionFileChooser = new FileChooser(exportRegionDirectory);
@@ -94,7 +94,7 @@ public class RegionsBaseMenuAction extends MenuAction {
         }
 
         if (!roiFile.exists()) {
-            org.broad.igv.ui.util.UIUtilities.showErrorMessage(mainFrame, "Region of Interest export file not found!");
+            MessageUtils.showMessage("Region of Interest export file not found!");
             return;
         }
         try {
@@ -109,7 +109,8 @@ public class RegionsBaseMenuAction extends MenuAction {
                     String[] data = dataRecord.split("\t");
                     if (data.length >= 3) {
                         try {
-                            RegionOfInterest regionOfInterest = new RegionOfInterest(data[0], Integer.parseInt(data[1]), Integer.parseInt(data[2]), "ROI");
+                            String name = data.length > 3 ? data[3] : null;
+                            RegionOfInterest regionOfInterest = new RegionOfInterest(data[0], Integer.parseInt(data[1]), Integer.parseInt(data[2]), name);
                             mainFrame.addRegionOfInterest(regionOfInterest);
                         } catch (NumberFormatException numberFormatException) {
                         }
@@ -157,7 +158,7 @@ public class RegionsBaseMenuAction extends MenuAction {
             return;
         }
         try {
-            Collection<RegionOfInterest> regions = IGVModel.getInstance().getAllRegionsOfInterest();
+            Collection<RegionOfInterest> regions = IGVMainFrame.getInstance().getSession().getAllRegionsOfInterest();
 
             if (regions == null || regions.isEmpty()) {
                 return;
@@ -180,11 +181,19 @@ public class RegionsBaseMenuAction extends MenuAction {
                     }
 
                     // Write info in BED format
-                    writer.print(regionOfInterest.getChromosomeName());
+                    writer.print(regionOfInterest.getChr());
                     writer.print("\t");
                     writer.print(regionStart);
                     writer.print("\t");
-                    writer.println(regionEnd);
+                    writer.print(regionEnd);
+
+                    if (regionOfInterest.getDescription() != null) {
+                        writer.print("\t");
+                        writer.println(regionOfInterest.getDescription());
+                    }
+                    else {
+                        writer.println();
+                    }
                 }
             } finally {
 
diff --git a/src/org/broad/igv/ui/action/RemoveUserDefinedGenomeMenuAction.java b/src/org/broad/igv/ui/action/RemoveUserDefinedGenomeMenuAction.java
index 4d94368..807591c 100644
--- a/src/org/broad/igv/ui/action/RemoveUserDefinedGenomeMenuAction.java
+++ b/src/org/broad/igv/ui/action/RemoveUserDefinedGenomeMenuAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -28,10 +28,10 @@ package org.broad.igv.ui.action;
 import org.apache.log4j.Logger;
 import org.broad.igv.feature.GenomeManager;
 import org.broad.igv.feature.GenomeManager.GenomeListItem;
-import org.broad.igv.ui.GuiUtilities;
 import org.broad.igv.ui.IGVMainFrame;
 import org.broad.igv.ui.UIConstants;
 import org.broad.igv.ui.UserDefinedGenomeCheckList;
+import org.broad.igv.ui.util.UIUtilities;
 
 import javax.swing.*;
 import java.awt.event.ActionEvent;
@@ -63,7 +63,7 @@ public class RemoveUserDefinedGenomeMenuAction extends MenuAction {
     @Override
     public void actionPerformed(ActionEvent e) {
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
             public void run() {
                 removeGenome();
             }
@@ -75,7 +75,7 @@ public class RemoveUserDefinedGenomeMenuAction extends MenuAction {
         try {
 
             LinkedHashSet<GenomeListItem> genomeItemList =
-                    GenomeManager.getInstance().getUserDefinedGenomeArchiveList(null);
+                    GenomeManager.getInstance().getUserDefinedGenomeArchiveList();
 
             if (genomeItemList.isEmpty()) {
                 JOptionPane.showMessageDialog(IGVMainFrame.getInstance(),
diff --git a/src/org/broad/igv/ui/action/ResetPreferencesAction.java b/src/org/broad/igv/ui/action/ResetPreferencesAction.java
index 79f6a89..3ae4818 100644
--- a/src/org/broad/igv/ui/action/ResetPreferencesAction.java
+++ b/src/org/broad/igv/ui/action/ResetPreferencesAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,9 +22,9 @@
  */
 package org.broad.igv.ui.action;
 
-import org.broad.igv.ui.GuiUtilities;
 import org.broad.igv.ui.IGVMainFrame;
 import org.broad.igv.ui.UIConstants;
+import org.broad.igv.ui.util.UIUtilities;
 
 import javax.swing.*;
 import java.awt.event.ActionEvent;
@@ -46,7 +46,7 @@ public class ResetPreferencesAction extends MenuAction {
 
     @Override
     public void actionPerformed(ActionEvent e) {
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
             public void run() {
                 execute();
 
diff --git a/src/org/broad/igv/ui/action/SaveSessionMenuAction.java b/src/org/broad/igv/ui/action/SaveSessionMenuAction.java
index f3f507b..f23a796 100644
--- a/src/org/broad/igv/ui/action/SaveSessionMenuAction.java
+++ b/src/org/broad/igv/ui/action/SaveSessionMenuAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -24,15 +24,16 @@ package org.broad.igv.ui.action;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
+import org.broad.igv.Globals;
 import org.broad.igv.PreferenceManager;
 import org.broad.igv.session.Session;
 import org.broad.igv.session.SessionWriter;
 import org.broad.igv.ui.IGVMainFrame;
-import org.broad.igv.ui.IGVModel;
-import org.broad.igv.ui.LongRunningTask;
 import org.broad.igv.ui.UIConstants;
+import org.broad.igv.ui.WaitCursorManager;
 import org.broad.igv.ui.util.FileChooserDialog;
+import org.broad.igv.util.LongRunningTask;
+import org.broad.igv.util.NamedRunnable;
 
 import javax.swing.*;
 import java.awt.event.ActionEvent;
@@ -76,7 +77,7 @@ public class SaveSessionMenuAction extends MenuAction {
         File sessionFile = null;
         FileChooserDialog dialog = null;
 
-        String currentSessionFilePath = mainFrame.getCurrentSessionFilePath();
+        String currentSessionFilePath = mainFrame.getSession().getPath();
 
         // If no previous session file or we're doing a save as
         if (true) // currentSessionFilePath == null)
@@ -94,7 +95,7 @@ public class SaveSessionMenuAction extends MenuAction {
             }
 
             if (dialog.getSelectedFile() == null) {
-                dialog.setSelectedFile(new File(IGVConstants.DEFAULT_SESSION_FILE));
+                dialog.setSelectedFile(new File(UIConstants.DEFAULT_SESSION_FILE));
             }
 
             dialog.setCurrentDirectory(PreferenceManager.getInstance().getLastSessionDirectory());
@@ -114,10 +115,10 @@ public class SaveSessionMenuAction extends MenuAction {
             sessionFile = file;
 
             String filePath = file.getAbsolutePath();
-            if (filePath.toLowerCase().endsWith(IGVConstants.SESSION_FILE_EXTENSION)) {    // Its ok
+            if (filePath.toLowerCase().endsWith(Globals.SESSION_FILE_EXTENSION)) {    // Its ok
                 sessionFile = file;
             } else {
-                sessionFile = new File(filePath + IGVConstants.SESSION_FILE_EXTENSION);
+                sessionFile = new File(filePath + Globals.SESSION_FILE_EXTENSION);
             }
 
         } else {
@@ -131,43 +132,36 @@ public class SaveSessionMenuAction extends MenuAction {
             sessionFile.delete();
         }
 
-
         final File sf = sessionFile;
-        LongRunningTask.submit(new Runnable() {
-
-            public void run() {
-                FileOutputStream out = null;
-                try {
-
-                    Session session = new Session(sf.getAbsolutePath());
-                    session.setLocus(
-                            IGVModel.getInstance().getViewContext().getCurrentLocusString());
-                    session.setFilter(IGVMainFrame.getInstance().getTrackFilter());
-                    (new SessionWriter()).saveSession(session, sf);
 
-                    mainFrame.setCurrentSessionFilePath(sf.getAbsolutePath());
+        FileOutputStream out = null;
+        WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
+        try {
 
-                    // No errors so save last location
-                    PreferenceManager.getInstance().setLastSessionDirectory(sf.getParentFile());
+            Session currentSession = mainFrame.getSession();
+            currentSession.setFilePath(sf.getAbsolutePath());
+            (new SessionWriter()).saveSession(currentSession, sf);
 
-                } catch (Exception e) {
-                    JOptionPane.showMessageDialog(mainFrame, "There was an error writing to " + sf.getName() + "(" + e.getMessage() + ")");
-                    log.error("Failed to save session!", e);
-                } finally {
+            // No errors so save last location
+            PreferenceManager.getInstance().setLastSessionDirectory(sf.getParentFile());
 
-                    mainFrame.resetStatusMessage();
+        } catch (Exception e2) {
+            JOptionPane.showMessageDialog(mainFrame, "There was an error writing to " + sf.getName() + "(" + e2.getMessage() + ")");
+            log.error("Failed to save session!", e2);
+        } finally {
+            WaitCursorManager.removeWaitCursor(token);
+            mainFrame.resetStatusMessage();
 
-                    if (out != null) {
+            if (out != null) {
 
-                        try {
-                            out.close();
-                        } catch (IOException exception) {
-                            log.error("Failed to close session file!", exception);
-                        }
-                    }
+                try {
+                    out.close();
+                } catch (IOException exception) {
+                    log.error("Failed to close session file!", exception);
                 }
             }
-        });
+        }
+
 
     }
 }
diff --git a/src/org/broad/igv/ui/action/SearchCommand.java b/src/org/broad/igv/ui/action/SearchCommand.java
index 2238237..a1b33f3 100644
--- a/src/org/broad/igv/ui/action/SearchCommand.java
+++ b/src/org/broad/igv/ui/action/SearchCommand.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -24,14 +24,14 @@ package org.broad.igv.ui.action;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
+import org.broad.igv.Globals;
 import org.broad.igv.PreferenceManager;
 import org.broad.igv.feature.Chromosome;
 import org.broad.igv.feature.Feature;
 import org.broad.igv.feature.FeatureDB;
-import org.broad.igv.track.TrackManager;
+import org.broad.igv.feature.Genome;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.ui.IGVMainFrame;
-import org.broad.igv.ui.LongRunningTask;
-import org.broad.igv.ui.ViewContext;
 import org.broad.igv.ui.util.MessageUtils;
 
 /**
@@ -50,155 +50,140 @@ public class SearchCommand implements Command {
 
     String searchString;
     ViewContext viewContext;
+    boolean recordHistory = true;
+    Genome genome;
 
-    /**
-     * Constructs ...
-     *
-     * @param viewContext
-     * @param searchString
-     */
     public SearchCommand(ViewContext viewContext, String searchString) {
         this.viewContext = viewContext;
         this.searchString = searchString.trim();
+        genome = viewContext.getGenome();
     }
 
-
-    public void execute() {
-        execute(true);
+    public SearchCommand(ViewContext viewContext, String searchString, boolean recordHistory) {
+        this(viewContext, searchString);
+        this.recordHistory = recordHistory;
     }
 
-    /**
-     * Method description
-     */
-    public void execute(boolean async) {
 
-        Runnable runnable = getRunnable();
+    public void execute() {
 
-        if (async) {
-            LongRunningTask.submit(runnable);
-        } else {
-            runnable.run();
+        if (log.isDebugEnabled()) {
+            log.debug("Run search: " + searchString);
         }
-    }
 
-    private Runnable getRunnable() {
-        return new Runnable() {
+        boolean success = false;
+
+        // Space delimited?
+        String[] tokens = searchString.split("\\s+");
+        if (tokens.length == 3) {
+            String chr = tokens[0];
+            String chrAlias = genome == null ? chr :  genome.getChromosomeAlias(chr);
+            int start = Integer.parseInt(tokens[1]) - 1; // Convert to UCSC convention
+            int end = Integer.parseInt(tokens[2]);
+            viewContext.jumpTo(chrAlias, start, end);
+            success = true;
+        }
 
-            public void run() {
-                // The search string can be a feature (e.g. gene) name or a locus
-                // description.  Try feature name first
+        // Feature search
 
-                if (log.isDebugEnabled()) {
-                    log.debug("Run search: " + searchString);
+        else {
+            Feature feature = FeatureDB.getFeature(searchString.toUpperCase().trim());
+            if (feature != null) {
+                int start = Math.max(0, feature.getStart());
+                int end = feature.getEnd();
+
+                if (PreferenceManager.getInstance().getBooleanPreference(PreferenceManager.SEARCH_ZOOM, true)) {
+                    viewContext.jumpTo(feature.getChr(), start, end);
+                } else {
+                    int center = (start + end) / 2;
+                    viewContext.centerOnLocation(feature.getChr(), center);
                 }
 
-                // Feature search
-                Feature feature = FeatureDB.getFeature(searchString.toUpperCase().trim());
-                if (feature != null) {
-                    int start = Math.max(0, feature.getStart());
-                    int end = feature.getEnd();
+                if (log.isDebugEnabled()) {
+                    log.debug("End search: " + searchString);
+                }
+                success = true;
+            }
 
-                    if (PreferenceManager.getInstance().getBooleanPreference(PreferenceManager.SEARCH_ZOOM, true)) {
-                        viewContext.jumpTo(feature.getChromosome(), start, end);
-                    }
-                    else {
-                        int center = (start + end) / 2;
-                        viewContext.centerOnLocation(feature.getChromosome(), center);
-                    }
-                    TrackManager.getInstance().preloadSAMTracks();
 
+            // Apparently not a feature. Either a locus or track name.  Track names can be quoted,
+            // loci are never quoted.
+            else if (!searchString.contains("\"")) {
+                String chr = null;
+                int[] startEnd = null;
+                int colon = searchString.indexOf(":");
 
-                    if (log.isDebugEnabled()) {
-                        log.debug("End search: " + searchString);
-                    }
-                    return;
-                }
+                if (colon > 0) {
 
+                    // The chromosome is that portion of the search string up to the colon.
+                    chr = searchString.substring(0, colon);
+                    String chrAlias = genome == null ? chr :  genome.getChromosomeAlias(chr);
+                    String posString = searchString.substring(colon).replace(":", "");
+                    startEnd = getStartEnd(posString);
 
-                // Apparently not a feature. Either a locus or track name.  Track names can be quoted,
-                // loci are never quoted.
-                if (!searchString.contains("\"")) {
-                    String chr = null;
-                    int[] startEnd = null;
-                    int colon = searchString.indexOf(":");
+                    if (startEnd != null) {
+                        viewContext.jumpTo(chrAlias, startEnd[0], startEnd[1]);
 
-                    if (colon > 0) {
+                        if (log.isDebugEnabled()) {
+                            log.debug("End search: " + searchString);
+                        }
+                        success = true;
+                    }
+                } else {
 
-                        // The chromosome is that portion of the search string up to the colon.
-                        chr = searchString.substring(0, colon);
-                        String posString = searchString.substring(colon).replace(":", "");
-                        startEnd = getStartEnd(posString);
+                    // No chromosome delimiter (color),  The search string is either chromosome name
+                    // or a locus in the current chromosome.
+                    if (searchString.contains("-")) {
 
+                        // Presense of a dash indicates this is a locus string in the current chromosome
+                        startEnd = getStartEnd(searchString);
                         if (startEnd != null) {
-                            viewContext.jumpTo(chr, startEnd[0], startEnd[1]);
+                            viewContext.jumpTo(null, startEnd[0], startEnd[1]);
 
                             if (log.isDebugEnabled()) {
                                 log.debug("End search: " + searchString);
                             }
-                            return;
+                            success = true;
+
                         }
                     } else {
 
-                        // No chromosome delimiter (color),  The search string is either chromosome name
-                        // or a locus in the current chromosome.
-                        if (searchString.contains("-")) {
-
-                            // Presense of a dash indicates this is a locus string in the current chromosome
-                            startEnd = getStartEnd(searchString);
-                            if (startEnd != null) {
-                                viewContext.jumpTo(null, startEnd[0], startEnd[1]);
-                                TrackManager.getInstance().preloadSAMTracks();
-
-                                if (log.isDebugEnabled()) {
-                                    log.debug("End search: " + searchString);
-                                }
-                                return;
-
-                            }
-                        } else {
-
-                            // No dash, this is either a chromosome or an unkown search string
-                            Chromosome chromosome = viewContext.getGenome().getChromosome(searchString);
-                            if (chromosome != null) {
-                                viewContext.setChromosomeName(searchString, true);
-                                TrackManager.getInstance().preloadSAMTracks();
-
-                                if (log.isDebugEnabled()) {
-                                    log.debug("End search: " + searchString);
-                                }
-                                return;
-                            }
-
-                            // Might be tab delimited chr, start, end
-                            String[] tokens = searchString.split("\t");
-                            if (tokens.length > 2) {
-                                chr = tokens[0];
-                                int start = Integer.parseInt(tokens[1]) - 1;
-                                int end = Integer.parseInt(tokens[2]);
-                                viewContext.jumpTo(chr, start, end);
-                                TrackManager.getInstance().preloadSAMTracks();
-
-                                if (log.isDebugEnabled()) {
-                                    log.debug("End search: " + searchString);
-                                }
-                                return;
-
+                        // No dash, this is either a chromosome or an unkown search string
+                        String chrAlias = genome == null ? searchString :  genome.getChromosomeAlias(searchString);
+                        Chromosome chromosome = viewContext.getGenome().getChromosome(chrAlias);
+                        if (chromosome != null || chrAlias.equals(Globals.CHR_ALL)) {
+                            viewContext.setChromosomeName(chrAlias, true);
+                            IGVMainFrame.getInstance().repaintDataAndHeaderPanels();
+                            IGVMainFrame.getInstance().repaintStatusAndZoomSlider();
+                            
 
+                            if (log.isDebugEnabled()) {
+                                log.debug("End search: " + searchString);
                             }
+                            success = true;
+                            ;
                         }
-                    }
-                }
 
-                if (!IGVMainFrame.getInstance().scrollToTrack(searchString.replaceAll("\"", ""))) {
-                    showError("Cannot find feature or locus: " + searchString);
+                    }
                 }
+            }
+        }
 
-                if (log.isDebugEnabled()) {
-                    log.debug("End search: " + searchString);
-                }
 
+        if (success) {
+           if(recordHistory) {
+               viewContext.history.push(searchString);
+           }
+        } else {
+            if (!IGVMainFrame.getInstance().scrollToTrack(searchString.replaceAll("\"", ""))) {
+                showError("Cannot find feature or locus: " + searchString);
             }
-        };
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("End search: " + searchString);
+        }
+
     }
 
     /**
diff --git a/src/org/broad/igv/ui/action/SetOverlayDatasetAction.java b/src/org/broad/igv/ui/action/SetOverlayDatasetAction.java
deleted file mode 100644
index 0c986f7..0000000
--- a/src/org/broad/igv/ui/action/SetOverlayDatasetAction.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.ui.action;
-
-import org.apache.commons.math.stat.StatUtils;
-import org.apache.log4j.Logger;
-import org.broad.igv.PreferenceManager;
-import org.broad.igv.track.Track;
-import org.broad.igv.track.TrackManager;
-import org.broad.igv.ui.GuiUtilities;
-import org.broad.igv.ui.IGVMainFrame;
-import org.broad.igv.ui.UIConstants;
-
-import javax.swing.*;
-import java.awt.*;
-import java.awt.event.ActionEvent;
-import java.util.List;
-
-/**
- * @author jrobinso
- */
-public class SetOverlayDatasetAction extends MenuAction {
-
-    static Logger log = Logger.getLogger(SetOverlayDatasetAction.class);
-    static int lastTrackHeight = -1;
-    IGVMainFrame mainFrame;
-
-    public SetOverlayDatasetAction(String label, IGVMainFrame mainFrame) {
-        super(label);
-        this.mainFrame = mainFrame;
-        setToolTipText(UIConstants.SET_DEFAULT_TRACK_HEIGHT_TOOLTIP);
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-
-        GuiUtilities.invokeOnEventThread(new Runnable() {
-
-            public void run() {
-
-                JPanel container = new JPanel();
-                JLabel label = new JLabel("Overlay Dataset: ");
-                JTextField datasetNameField = new JTextField();
-                Dimension preferredSize = datasetNameField.getPreferredSize();
-                datasetNameField.setPreferredSize(
-                        new Dimension(50, (int) preferredSize.getHeight()));
-                container.add(label);
-                container.add(datasetNameField);
-
-                datasetNameField.setText(TrackManager.OVERLAY_DATASET);
-
-                int status =
-                        JOptionPane.showConfirmDialog(
-                                mainFrame,
-                                container,
-                                "Set Track Height",
-                                JOptionPane.OK_CANCEL_OPTION,
-                                JOptionPane.PLAIN_MESSAGE,
-                                null);
-
-                if (status == JOptionPane.CANCEL_OPTION ||
-                        status == JOptionPane.CLOSED_OPTION) {
-                    return;
-                }
-
-                try {
-                    TrackManager.OVERLAY_DATASET = datasetNameField.getText().trim();
-                    mainFrame.updateTrackState();
-                } catch (NumberFormatException numberFormatException) {
-                    JOptionPane.showMessageDialog(mainFrame, "Track height must be an integer number.");
-                }
-
-
-            }
-        });
-    }
-
-    /**
-     * Return a representative track height to use as the default.  For now
-     * using the median track height.
-     *
-     * @return
-     */
-    private int getRepresentativeTrackHeight() {
-
-        if (lastTrackHeight > 0) {
-            return lastTrackHeight;
-        }
-
-        // Get all tracks except the gene track
-        List<Track> tracks = TrackManager.getInstance().getAllTracks(false);
-
-
-        double[] heights = new double[tracks.size()];
-        for (int i = 0; i < tracks.size(); i++) {
-            heights[i] = tracks.get(i).getHeight();
-        }
-        int medianTrackHeight = (int) Math.round(StatUtils.percentile(heights, 50));
-        if (medianTrackHeight > 0) {
-            return medianTrackHeight;
-        }
-
-        return PreferenceManager.getInstance().getDefaultTrackHeight();
-
-    }
-}
diff --git a/src/org/broad/igv/ui/action/SetTrackHeightMenuAction.java b/src/org/broad/igv/ui/action/SetTrackHeightMenuAction.java
index 7cd6636..ff08fe8 100644
--- a/src/org/broad/igv/ui/action/SetTrackHeightMenuAction.java
+++ b/src/org/broad/igv/ui/action/SetTrackHeightMenuAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -29,7 +29,6 @@ import org.apache.commons.math.stat.StatUtils;
 import org.apache.log4j.Logger;
 import org.broad.igv.PreferenceManager;
 import org.broad.igv.track.Track;
-import org.broad.igv.track.TrackManager;
 import org.broad.igv.ui.IGVMainFrame;
 import org.broad.igv.ui.UIConstants;
 
@@ -99,7 +98,7 @@ public class SetTrackHeightMenuAction extends MenuAction {
 
             try {
                 int newTrackHeight = Integer.parseInt(trackHeightField.getText().trim());
-                TrackManager.getInstance().setAllTrackHeights(newTrackHeight);
+                IGVMainFrame.getInstance().getTrackManager().setAllTrackHeights(newTrackHeight);
                 lastTrackHeight = newTrackHeight;
                 doRefresh = true;
             }
@@ -136,7 +135,7 @@ public class SetTrackHeightMenuAction extends MenuAction {
         }
 
         // Get all tracks except the gene track
-        List<Track> tracks = TrackManager.getInstance().getAllTracks(false);
+        List<Track> tracks = IGVMainFrame.getInstance().getTrackManager().getAllTracks(false);
 
 
         double[] heights = new double[tracks.size()];
diff --git a/src/org/broad/igv/ui/action/SortTracksMenuAction.java b/src/org/broad/igv/ui/action/SortTracksMenuAction.java
index 13bfc88..d2e5134 100644
--- a/src/org/broad/igv/ui/action/SortTracksMenuAction.java
+++ b/src/org/broad/igv/ui/action/SortTracksMenuAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,11 +23,10 @@
 package org.broad.igv.ui.action;
 
 import org.broad.igv.track.AttributeManager;
-import org.broad.igv.track.TrackManager;
-import org.broad.igv.ui.GuiUtilities;
 import org.broad.igv.ui.IGVMainFrame;
 import org.broad.igv.ui.UIConstants;
 import org.broad.igv.ui.util.SortDialog;
+import org.broad.igv.ui.util.UIUtilities;
 
 import java.awt.event.ActionEvent;
 import java.util.Collections;
@@ -49,7 +48,7 @@ public class SortTracksMenuAction extends MenuAction {
 
     @Override
     public void actionPerformed(ActionEvent e) {
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
                 doSortTrackByAttribute();
@@ -61,10 +60,10 @@ public class SortTracksMenuAction extends MenuAction {
     final public void doSortTrackByAttribute() {
 
         List<String> keys = AttributeManager.getInstance().getAttributeKeys();
-        if (keys != null) {
-            Collections.sort(keys,
-                    AttributeManager.getInstance().getAttributeComparator());
-        }
+        //if (keys != null) {
+        //    Collections.sort(keys,
+        //            AttributeManager.getInstance().getAttributeComparator());
+        //}
         Object availableSortKeys[] = keys.toArray();
         SortDialog dialog = new SortDialog(mainFrame, true, availableSortKeys);
         dialog.setVisible(true);
@@ -75,9 +74,8 @@ public class SortTracksMenuAction extends MenuAction {
 
         String[] selectedSortKeys = dialog.getSelectedSortKeys();
         if (selectedSortKeys != null) {
-            TrackManager.getInstance().sortAllTracksByAttributes(
+            IGVMainFrame.getInstance().getTrackManager().sortAllTracksByAttributes(
                     selectedSortKeys, dialog.isAscending());
-            mainFrame.fireViewChangedEvent();
             mainFrame.repaint();
         }
 
diff --git a/src/org/broad/igv/ui/dnd/AbstractGhostDropManager.java b/src/org/broad/igv/ui/dnd/AbstractGhostDropManager.java
index cf6a3ba..58acf22 100644
--- a/src/org/broad/igv/ui/dnd/AbstractGhostDropManager.java
+++ b/src/org/broad/igv/ui/dnd/AbstractGhostDropManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/dnd/GhostDropAdapter.java b/src/org/broad/igv/ui/dnd/GhostDropAdapter.java
index 4adb4b4..535cd84 100644
--- a/src/org/broad/igv/ui/dnd/GhostDropAdapter.java
+++ b/src/org/broad/igv/ui/dnd/GhostDropAdapter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/dnd/GhostDropEvent.java b/src/org/broad/igv/ui/dnd/GhostDropEvent.java
index eb342c5..3cf65c6 100644
--- a/src/org/broad/igv/ui/dnd/GhostDropEvent.java
+++ b/src/org/broad/igv/ui/dnd/GhostDropEvent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -18,15 +18,52 @@
 
 package org.broad.igv.ui.dnd;
 
-import java.awt.*;
+import org.broad.igv.track.Track;
+import org.broad.igv.ui.panel.TrackPanel;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.awt.Point;
+
 
 public class GhostDropEvent {
     private Point point;
     private Point startPoint;
+    List<Track> tracks;
+    List<TrackPanel> sourcePanels;
+    boolean tracksDropped = false;
 
-    public GhostDropEvent(Point startPoint, Point point) {
+
+    public GhostDropEvent(Point startPoint, Point point, java.util.List<Track> tracks) {
         this.startPoint = startPoint;
         this.point = point;
+        this.tracks = tracks;
+        sourcePanels = new ArrayList();
+    }
+
+    public void addSourcePanel(TrackPanel panel) {
+        sourcePanels.add(panel);
+    }
+
+    public void setTracksDropped(boolean bool) {
+        tracksDropped = bool;
+    }
+
+    public boolean isTracksDropped() {
+        return tracksDropped;
+    }
+
+    /**
+     * Called when the destination panel is found.  If the tracks are dropped outside a valid destination panel
+     * this is never called
+     */
+    public void removeTracksFromSource() {
+
+        for(TrackPanel panel : sourcePanels) {
+            panel.removeTracks(tracks);
+        }
+        sourcePanels.clear();
+
     }
 
     public Point getStartLocation() {
@@ -36,4 +73,8 @@ public class GhostDropEvent {
     public Point getDropLocation() {
         return point;
     }
+
+    public java.util.List<Track> getTracks() {
+        return tracks;
+    }
 }
diff --git a/src/org/broad/igv/ui/dnd/GhostDropListener.java b/src/org/broad/igv/ui/dnd/GhostDropListener.java
index bd2c840..c62b8d9 100644
--- a/src/org/broad/igv/ui/dnd/GhostDropListener.java
+++ b/src/org/broad/igv/ui/dnd/GhostDropListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/dnd/GhostGlassPane.java b/src/org/broad/igv/ui/dnd/GhostGlassPane.java
index 0ccf621..7900e02 100644
--- a/src/org/broad/igv/ui/dnd/GhostGlassPane.java
+++ b/src/org/broad/igv/ui/dnd/GhostGlassPane.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/event/GlobalKeyDispatcher.java b/src/org/broad/igv/ui/event/GlobalKeyDispatcher.java
index be05df7..3199ef0 100644
--- a/src/org/broad/igv/ui/event/GlobalKeyDispatcher.java
+++ b/src/org/broad/igv/ui/event/GlobalKeyDispatcher.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,24 +22,25 @@
  */
 package org.broad.igv.ui.event;
 
+import org.broad.igv.Globals;
 import org.broad.igv.feature.Feature;
-import org.broad.igv.feature.FeatureUtils;
-import org.broad.igv.feature.LocusScore;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.track.FeatureTrack;
 import org.broad.igv.track.GeneTrack;
 import org.broad.igv.track.Track;
-import org.broad.igv.track.TrackManager;
+import org.broad.igv.ui.IGVCommandBar;
 import org.broad.igv.ui.IGVMainFrame;
-import org.broad.igv.ui.IGVModel;
-import org.broad.igv.ui.ViewContext;
+import org.broad.igv.ui.RegionOfInterest;
+import org.broad.igv.ui.UIConstants;
+import org.broad.igv.ui.action.SearchCommand;
 import org.broad.igv.ui.util.MessageUtils;
 
 import javax.swing.*;
 import java.awt.*;
 import java.awt.event.ActionEvent;
 import java.awt.event.KeyEvent;
+import java.io.IOException;
 import java.util.Collection;
-import java.util.List;
 
 /**
  * @author jrobinso
@@ -48,9 +49,11 @@ public class GlobalKeyDispatcher implements KeyEventDispatcher {
 
     private final InputMap keyStrokes = new InputMap();
     private final ActionMap actions = new ActionMap();
+    private IGVMainFrame mainFrame;
 
     public GlobalKeyDispatcher() {
         init();
+        mainFrame = IGVMainFrame.getInstance();
     }
 
     public InputMap getInputMap() {
@@ -64,12 +67,12 @@ public class GlobalKeyDispatcher implements KeyEventDispatcher {
     public boolean dispatchKeyEvent(KeyEvent event) {
 
         if (event.getKeyCode() == KeyEvent.VK_ESCAPE) {
-            TrackManager.getInstance().clearSelections();
+            IGVMainFrame.getInstance().getTrackManager().clearSelections();
             IGVMainFrame.getInstance().repaint();
             return true;
         }
 
-        KeyStroke ks = KeyStroke.getKeyStrokeForEvent((KeyEvent) event);
+        KeyStroke ks = KeyStroke.getKeyStrokeForEvent(event);
         String actionKey = (String) keyStrokes.get(ks);
 
 
@@ -95,18 +98,26 @@ public class GlobalKeyDispatcher implements KeyEventDispatcher {
     }
 
     // Here for convenience,  move out of this class eventually
+
     public void init() {
 
         final KeyStroke nextKey = KeyStroke.getKeyStroke(KeyEvent.VK_F, KeyEvent.CTRL_MASK, false);
         final KeyStroke prevKey = KeyStroke.getKeyStroke(KeyEvent.VK_B, KeyEvent.CTRL_MASK, false);
         final KeyStroke toolsKey = KeyStroke.getKeyStroke(KeyEvent.VK_T, KeyEvent.ALT_MASK, false);
+        final KeyStroke regionKey = KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.CTRL_MASK, false);
+
+
+        KeyStroke backKey1 = KeyStroke.getKeyStroke(KeyEvent.VK_CLOSE_BRACKET , KeyEvent.META_DOWN_MASK, false);
+        KeyStroke backKey2 = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.ALT_DOWN_MASK, false);
+        KeyStroke forwardKey1 =  KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET , KeyEvent.META_DOWN_MASK, false);
+        KeyStroke forwardKey2 =KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.ALT_DOWN_MASK, false);
 
 
         final Action toolAction = new AbstractAction() {
 
             public void actionPerformed(ActionEvent e) {
                 setEnabled(false); // stop any other events from interfering
-                IGVMainFrame.getInstance().enableToolMenu();
+                IGVMainFrame.getInstance().enableExtrasMenu();
                 setEnabled(true);
             }
         };
@@ -127,6 +138,44 @@ public class GlobalKeyDispatcher implements KeyEventDispatcher {
                 setEnabled(true);
             }
         };
+        final Action regionAction = new AbstractAction() {
+
+            public void actionPerformed(ActionEvent e) {
+                setEnabled(false); // stop any other events from interfering
+                ViewContext.Range currentRange = ViewContext.getInstance().getCurrentRange();
+                RegionOfInterest regionOfInterest =
+                        new RegionOfInterest(
+                                currentRange.getChr(),
+                                currentRange.getStart(),
+                                currentRange.getEnd(),
+                                null);
+                // TODO -- get this ugly reference out of here
+                IGVMainFrame.getInstance().addRegionOfInterest(regionOfInterest);
+                setEnabled(true);
+            }
+        };
+
+        final Action backAction = new AbstractAction() {
+
+            public void actionPerformed(ActionEvent e) {
+                String locus = ViewContext.getInstance().history.back();
+                if (locus != null) {
+                    (new SearchCommand(ViewContext.getInstance(), locus, false)).execute();
+                    IGVMainFrame.getInstance().refreshCommandBar();
+
+                }
+            }
+        };
+        final Action forwardAction = new AbstractAction() {
+
+            public void actionPerformed(ActionEvent e) {
+                String locus = ViewContext.getInstance().history.forward();
+                if (locus != null) {
+                    (new SearchCommand(ViewContext.getInstance(), locus, false)).execute();
+                    IGVMainFrame.getInstance().refreshCommandBar();
+                }
+            }
+        };
 
         getInputMap().put(nextKey, "nextFeature");
         getActionMap().put("nextFeature", nextAction);
@@ -134,68 +183,63 @@ public class GlobalKeyDispatcher implements KeyEventDispatcher {
         getActionMap().put("prevFeature", prevAction);
         getInputMap().put(toolsKey, "tools");
         getActionMap().put("tools", toolAction);
+        getInputMap().put(regionKey, "region");
+        getActionMap().put("region", regionAction);
+
+        getInputMap().put(backKey1, "back");
+        getInputMap().put(backKey2, "back");
+        getActionMap().put("back", backAction);
+        getInputMap().put(forwardKey1, "forward");
+        getInputMap().put(forwardKey2, "forward");
+        getActionMap().put("forward", forwardAction);
 
     }
 
+
     private void nextFeature(boolean forward) {
+
         ViewContext vc = getViewContext();
-        Collection<Track> tracks = TrackManager.getInstance().getSelectedTracks();
+        Collection<Track> tracks = IGVMainFrame.getInstance().getTrackManager().getSelectedTracks();
         if (tracks.size() == 1) {
-            Track t = tracks.iterator().next();
-            if (!(t instanceof FeatureTrack || t instanceof GeneTrack)) {
-                //JOptionPane.showMessageDialog(IGVMainFrame.getInstance(),
-                //        "Track panning is not enabled for data tracks.");
-                return;
-            }
-
-            LocusScore f = null;
-
-            String chr = getViewContext().getChrName();
-            List<Feature> features = getFeatures(t, chr);
-            double center = getViewContext().getCenter();
+            try {
+                Track t = tracks.iterator().next();
+                if (!(t instanceof FeatureTrack || t instanceof GeneTrack)) {
+                    //JOptionPane.showMessageDialog(IGVMainFrame.getInstance(),
+                    //        "Track panning is not enabled for data tracks.");
+                    return;
+                }
 
-            boolean canScroll = (forward && !vc.windowAtEnd()) ||
-                    (!forward && vc.getOrigin() > 0);
+                Feature f = null;
+                if (t instanceof FeatureTrack) {
+                    f = ((FeatureTrack) t).nextFeature(vc.getChrName(), vc.getCenter(), forward);
+                } else if (t instanceof GeneTrack) {
+                    f = ((GeneTrack) t).nextFeature(vc.getChrName(), vc.getCenter(), forward);
+                }
 
-            if (features != null && features.size() > 0 && canScroll) {
-                f = forward ? FeatureUtils.getFeatureAfter(center + 1, features) : FeatureUtils.getFeatureBefore(center - 1, features);
-            }
-            if (f == null) {
-                String nextChr = chr;
-                while (f == null && (nextChr = forward ? vc.getNextChrName(nextChr) : vc.getPrevChrName(nextChr)) != null) {
-                    List<Feature> fl = getFeatures(t, nextChr);
-                    if (fl != null && fl.size() > 0) {
-                        int zoom = getViewContext().getZoom();
-                        getViewContext().setChromosomeName(nextChr);
-                        getViewContext().zoomAndCenter(zoom);
-                        f = forward ? fl.get(0) : fl.get(fl.size() - 1);
+                if (f != null) {
+                    String chr = vc.getGenome().getChromosomeAlias(f.getChr());
+                    double newCenter = f.getStart();
+                    if (!chr.equals(vc.getChrName())) {
+                        int zoom = vc.getZoom();
+                        vc.setChrName(chr);
+                        vc.zoomAndCenter(zoom);
                     }
+
+                    vc.centerOnLocation(newCenter);
                 }
-            }
+            } catch (IOException e) {
+                //logger.error("Error c")
+                MessageUtils.showMessage("Error encountered reading features: " + e.getMessage());
 
-            if (f != null) {
-                double newCenter = f.getStart();
-                getViewContext().centerOnLocation(newCenter);
             }
         } else {
             MessageUtils.showMessage("To use track panning you must first select a single feature track.");
         }
 
-    }
-
-    // Convenient method to hide some ugliness until refactoring
-    private List<Feature> getFeatures(Track track, String chr) {
-        if (track instanceof FeatureTrack) {
-            return ((FeatureTrack) track).getFeatures(chr);
-        } else if (track instanceof GeneTrack) {
-            return ((GeneTrack) track).getFeatures(chr);
-        } else {
-            return null;
-        }
 
     }
 
     private ViewContext getViewContext() {
-        return IGVModel.getInstance().getViewContext();
+        return ViewContext.getInstance();
     }
 }
diff --git a/src/org/broad/igv/ui/legend/ChrColorDialog.java b/src/org/broad/igv/ui/legend/ChrColorDialog.java
index 66c903c..3db0088 100644
--- a/src/org/broad/igv/ui/legend/ChrColorDialog.java
+++ b/src/org/broad/igv/ui/legend/ChrColorDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/legend/ChromosomeColorLegend.java b/src/org/broad/igv/ui/legend/ChromosomeColorLegend.java
index 59274bb..444f012 100644
--- a/src/org/broad/igv/ui/legend/ChromosomeColorLegend.java
+++ b/src/org/broad/igv/ui/legend/ChromosomeColorLegend.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/legend/HeatmapLegendEditor.java b/src/org/broad/igv/ui/legend/HeatmapLegendEditor.java
index 3021baf..b6faf4c 100644
--- a/src/org/broad/igv/ui/legend/HeatmapLegendEditor.java
+++ b/src/org/broad/igv/ui/legend/HeatmapLegendEditor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/legend/HeatmapLegendPanel.java b/src/org/broad/igv/ui/legend/HeatmapLegendPanel.java
index cc460e3..f0ae2f1 100644
--- a/src/org/broad/igv/ui/legend/HeatmapLegendPanel.java
+++ b/src/org/broad/igv/ui/legend/HeatmapLegendPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -30,8 +30,8 @@ import org.broad.igv.PreferenceManager;
 import org.broad.igv.renderer.ContinuousColorScale;
 import org.broad.igv.track.TrackType;
 import org.broad.igv.ui.FontManager;
-import org.broad.igv.ui.GuiUtilities;
 import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.util.UIUtilities;
 
 import javax.swing.*;
 import java.awt.*;
@@ -83,7 +83,7 @@ public class HeatmapLegendPanel extends LegendPanel {
      */
     public void doUserPreferences() {
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
 
@@ -103,9 +103,9 @@ public class HeatmapLegendPanel extends LegendPanel {
                 // TODO -- temporary hack.  We need some specific knowledge fo the implementation
                 // in order to edit it,  but do it without a cast
 
-                colorScale = (ContinuousColorScale) dialog.getColorScheme();
+                colorScale = dialog.getColorScheme();
                 PreferenceManager.getInstance().setColorScale(type, colorScale);
-
+                IGVMainFrame.getInstance().repaintDataPanels();
                 try {
 
                     reloadPreferences();
@@ -113,14 +113,12 @@ public class HeatmapLegendPanel extends LegendPanel {
                 }
                 finally {
 
-                    GuiUtilities.invokeOnEventThread(new Runnable() {
+                    UIUtilities.invokeOnEventThread(new Runnable() {
 
                         public void run() {
                             SwingUtilities.getWindowAncestor(HeatmapLegendPanel.this).toFront();
                         }
                     });
-
-                    IGVMainFrame.getInstance().clearImageCacheForTrackPanels();
                     IGVMainFrame.getInstance().resetStatusMessage();
                 }
             }
diff --git a/src/org/broad/igv/ui/legend/LegendDialog.java b/src/org/broad/igv/ui/legend/LegendDialog.java
index 8a6b58b..d9f79fe 100644
--- a/src/org/broad/igv/ui/legend/LegendDialog.java
+++ b/src/org/broad/igv/ui/legend/LegendDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/legend/LegendPanel.java b/src/org/broad/igv/ui/legend/LegendPanel.java
index 3f44010..af45d72 100644
--- a/src/org/broad/igv/ui/legend/LegendPanel.java
+++ b/src/org/broad/igv/ui/legend/LegendPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -26,10 +26,10 @@ package org.broad.igv.ui.legend;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.broad.igv.track.TrackType;
-import org.broad.igv.ui.GuiUtilities;
 import org.broad.igv.ui.IGVMainFrame;
 import org.broad.igv.ui.UIConstants;
 import org.broad.igv.ui.WaitCursorManager;
+import org.broad.igv.ui.util.UIUtilities;
 
 import javax.swing.*;
 import javax.swing.event.MouseInputAdapter;
@@ -70,7 +70,7 @@ abstract public class LegendPanel extends JPanel {
         };
         addMouseListener(mouseListener);
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
                 LegendPanel.this.setToolTipText(UIConstants.CLICK_ITEM_TO_EDIT_TOOLTIP);
@@ -86,8 +86,6 @@ abstract public class LegendPanel extends JPanel {
 
         }
         finally {
-
-            IGVMainFrame.getInstance().clearImageCacheForTrackPanels();
             IGVMainFrame.getInstance().resetStatusMessage();
         }
 
diff --git a/src/org/broad/igv/ui/legend/LohLegendPanel.java b/src/org/broad/igv/ui/legend/LohLegendPanel.java
index d8daf59..f596c21 100644
--- a/src/org/broad/igv/ui/legend/LohLegendPanel.java
+++ b/src/org/broad/igv/ui/legend/LohLegendPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/legend/MutationLegendPanel.java b/src/org/broad/igv/ui/legend/MutationLegendPanel.java
index 9441c78..7abb3b2 100644
--- a/src/org/broad/igv/ui/legend/MutationLegendPanel.java
+++ b/src/org/broad/igv/ui/legend/MutationLegendPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -29,11 +29,14 @@ import org.broad.igv.PreferenceManager;
 import org.broad.igv.feature.Mutation;
 import org.broad.igv.renderer.ColorScale;
 import org.broad.igv.ui.FontManager;
-import org.broad.igv.ui.GuiUtilities;
 import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.util.ColorTable;
 import org.broad.igv.ui.util.PropertyDialog;
 import org.broad.igv.ui.util.PropertyDialog.PreferenceDescriptor;
+
 import static org.broad.igv.ui.util.PropertyDialog.PreferenceType.COLOR;
+
+import org.broad.igv.ui.util.UIUtilities;
 import org.broad.igv.util.ColorUtilities;
 
 import javax.swing.*;
@@ -49,42 +52,26 @@ import java.util.Map;
  */
 public class MutationLegendPanel extends LegendPanel {
 
+    ColorTable colorTable;
 
-    /*
-     *     public HeatmapLegendPanel(TrackType type) {
-     *   this.type = type;
-     *   ColorScale colorScale = ColorScaleFactory.getColorScale(type);
-     *
-     *   //TODO -- temporary hack.  We need some specific knowledge fo the implementation
-     *   //in order to edit it,  but do it without a cast
-     *   this.colorScale = (ContinuousColorScale) colorScale;
-     * }
-     *
-     * protected void persistResetPreferences() {
-     *   ColorScaleFactory.setColorScheme(type, colorScale);
-     * }
-     *
-     * protected void resetPreferencesToDefault() {
-     *   colorScale = (ContinuousColorScale) ColorScaleFactory.getDefaultColorSchemeByType(type);
-     *   persistResetPreferences();
-     *   showResetDisplay();
-     * }
-     *
-     * protected void reloadPreferences() {
-     *   ColorScaleFactory.setColorScheme(type, colorScale);
-     *   HeatmapRenderer.clearColorSchemeCache();
-     *   repaint();
-     * }
-     */
+    public MutationLegendPanel() {
+        init();
+    }
+
+    private void init() {
+        ColorTable prefTable = PreferenceManager.getInstance().getMutationColorScheme();
+        colorTable = new ColorTable();
+        for (String key : prefTable.getKeys()) {
+            colorTable.put(key, prefTable.get(key));
+        }
+    }
 
     protected void persistResetPreferences() {
-        PreferenceManager.getInstance().setMutationColorScheme(Mutation.getColorScheme());
+        PreferenceManager.getInstance().setMutationColorScheme(colorTable);
     }
 
     protected void reloadPreferences() {
-        Mutation.reloadPreferences();
-
-        // ColorScaleFactory.clearCache();
+        init();
         repaint();
     }
 
@@ -96,7 +83,7 @@ public class MutationLegendPanel extends LegendPanel {
 
     @Override
     protected void resetPreferencesToDefault() {
-        Mutation.setColorScheme(Mutation.getDefaultColorScheme());
+
         persistResetPreferences();
         showResetDisplay();
     }
@@ -106,51 +93,44 @@ public class MutationLegendPanel extends LegendPanel {
      */
     public void doUserPreferences() {
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
-
-            public void run() {
 
-                IGVMainFrame.getInstance().setStatusBarMessage("Setting view properties...");
+        IGVMainFrame.getInstance().setStatusBarMessage("Setting view properties...");
 
-                // Add view preference items to the display
-                LinkedHashMap<String, PreferenceDescriptor> labelTextToKey = addPreferences();
+        // Add view preference items to the display
+        LinkedHashMap<String, PreferenceDescriptor> labelTextToKey = addPreferences();
 
-                Window window = SwingUtilities.getWindowAncestor(MutationLegendPanel.this);
+        Window window = SwingUtilities.getWindowAncestor(MutationLegendPanel.this);
 
-                // Create dialog
-                PropertyDialog dialog = new PropertyDialog(PreferenceManager.getInstance(),
-                        labelTextToKey, (Dialog) window, true);
+        // Create dialog
+        PropertyDialog dialog = new PropertyDialog(PreferenceManager.getInstance(),
+                labelTextToKey, (Dialog) window, true);
 
-                Component parent = IGVMainFrame.getInstance();
+        Component parent = IGVMainFrame.getInstance();
 
-                dialog.setLocationRelativeTo(parent);
-                dialog.setTitle("Color Preferences");
-                dialog.setVisible(true);
+        dialog.setLocationRelativeTo(parent);
+        dialog.setTitle("Color Preferences");
+        dialog.setVisible(true);
 
 
-                if (dialog.isCanceled()) {
-                    IGVMainFrame.getInstance().resetStatusMessage();
-                    return;
-                }
-
-                try {
-                    reloadPreferences();
+        if (dialog.isCanceled()) {
+            IGVMainFrame.getInstance().resetStatusMessage();
+            return;
+        }
 
-                }
-                finally {
+        try {
+            reloadPreferences();
 
-                    GuiUtilities.invokeOnEventThread(new Runnable() {
-                        public void run() {
-                            SwingUtilities.getWindowAncestor(MutationLegendPanel.this).toFront();
-                        }
-                    });
+        }
+        finally {
 
-                    IGVMainFrame.getInstance().clearImageCacheForTrackPanels();
-                    IGVMainFrame.getInstance().resetStatusMessage();
+            UIUtilities.invokeOnEventThread(new Runnable() {
+                public void run() {
+                    SwingUtilities.getWindowAncestor(MutationLegendPanel.this).toFront();
                 }
+            });
+            IGVMainFrame.getInstance().resetStatusMessage();
+        }
 
-            }
-        });
     }
 
     protected LinkedHashMap<String, PreferenceDescriptor> addPreferences() {
@@ -169,10 +149,9 @@ public class MutationLegendPanel extends LegendPanel {
         return labelTextToKey;
     }
 
-    protected void addNonsenseColorPreference(LinkedHashMap<String,
-            PreferenceDescriptor> labelTextToKey) {
+    protected void addNonsenseColorPreference(LinkedHashMap<String, PreferenceDescriptor> labelTextToKey) {
 
-        Color color = Mutation.getColorScheme().get(Mutation.Type.Nonsense);
+        Color color = colorTable.get("Nonsense");
         String labelText = "Nonsense Color: ";
 
         labelTextToKey.put(labelText,
@@ -180,10 +159,9 @@ public class MutationLegendPanel extends LegendPanel {
                         COLOR, ColorUtilities.convertColorToRGBString(color)));
     }
 
-    protected void addIndelColorPreference(LinkedHashMap<String,
-            PreferenceDescriptor> labelTextToKey) {
+    protected void addIndelColorPreference(LinkedHashMap<String, PreferenceDescriptor> labelTextToKey) {
 
-        Color color = Mutation.getColorScheme().get(Mutation.Type.Indel);
+        Color color = colorTable.get("Indel");
         String labelText = "Indel Color: ";
 
         labelTextToKey.put(labelText,
@@ -191,23 +169,20 @@ public class MutationLegendPanel extends LegendPanel {
                         COLOR, ColorUtilities.convertColorToRGBString(color)));
     }
 
-    protected void addTargetRegionColorPreference(LinkedHashMap<String,
-            PreferenceDescriptor> labelTextToKey) {
+    protected void addTargetRegionColorPreference(LinkedHashMap<String, PreferenceDescriptor> labelTextToKey) {
 
-        Color color = Mutation.getColorScheme().get(Mutation.Type.Targeted_Region);
+        Color color = colorTable.get("Targeted_Region");
         String labelText = "Target Region Color: ";
 
-        labelTextToKey.put(
-                labelText,
+        labelTextToKey.put(labelText,
                 new PreferenceDescriptor(
                         PreferenceManager.MUTATION_TARGETED_REGION_COLOR_KEY, COLOR,
                         ColorUtilities.convertColorToRGBString(color)));
     }
 
-    protected void addMissenseColorPreference(LinkedHashMap<String,
-            PreferenceDescriptor> labelTextToKey) {
+    protected void addMissenseColorPreference(LinkedHashMap<String, PreferenceDescriptor> labelTextToKey) {
 
-        Color color = Mutation.getColorScheme().get(Mutation.Type.Missense);
+        Color color = colorTable.get("Missense");
         String labelText = "Missense Color: ";
 
         labelTextToKey.put(labelText,
@@ -215,10 +190,9 @@ public class MutationLegendPanel extends LegendPanel {
                         COLOR, ColorUtilities.convertColorToRGBString(color)));
     }
 
-    protected void addSpliceSiteColorPreference(LinkedHashMap<String,
-            PreferenceDescriptor> labelTextToKey) {
+    protected void addSpliceSiteColorPreference(LinkedHashMap<String, PreferenceDescriptor> labelTextToKey) {
 
-        Color color = Mutation.getColorScheme().get(Mutation.Type.Splice_site);
+        Color color = colorTable.get("Splice_site");
         String labelText = "Splice Site Color: ";
 
         labelTextToKey.put(
@@ -228,10 +202,9 @@ public class MutationLegendPanel extends LegendPanel {
                         ColorUtilities.convertColorToRGBString(color)));
     }
 
-    protected void addSynonymousColorPreference(LinkedHashMap<String,
-            PreferenceDescriptor> labelTextToKey) {
+    protected void addSynonymousColorPreference(LinkedHashMap<String, PreferenceDescriptor> labelTextToKey) {
 
-        Color color = Mutation.getColorScheme().get(Mutation.Type.Synonymous);
+        Color color = colorTable.get("Synonymous");
         String labelText = "Synonymous Color: ";
 
         labelTextToKey.put(
@@ -241,10 +214,9 @@ public class MutationLegendPanel extends LegendPanel {
                         ColorUtilities.convertColorToRGBString(color)));
     }
 
-    protected void addUnknownColorPreference(LinkedHashMap<String,
-            PreferenceDescriptor> labelTextToKey) {
+    protected void addUnknownColorPreference(LinkedHashMap<String, PreferenceDescriptor> labelTextToKey) {
 
-        Color color = Mutation.getColorScheme().get(Mutation.Type.Unknown);
+        Color color = colorTable.get("Unknown");
         String labelText = "Unknown Color: ";
 
         labelTextToKey.put(labelText,
@@ -260,9 +232,8 @@ public class MutationLegendPanel extends LegendPanel {
     @Override
     public void paintLegend(Graphics g) {
 
-        Map<Mutation.Type, Color> theColorScheme = Mutation.getColorScheme();
 
-        if (theColorScheme == null) {
+        if (colorTable == null) {
             return;
         }
 
@@ -281,10 +252,10 @@ public class MutationLegendPanel extends LegendPanel {
             int y = lineHeight;
             int colCount = 0;
 
-            for (Map.Entry<Mutation.Type, Color> entry : theColorScheme.entrySet()) {
+            for (Map.Entry<String, Color> entry : colorTable.entrySet()) {
 
-                Mutation.Type mutType = entry.getKey();
-                String label = mutType.toString().replace("_", " ");
+                String mutType = entry.getKey();
+                String label = mutType.replace("_", " ");
                 int labelWidth = (int) fm.getStringBounds(label, g2D).getWidth();
 
                 g2D.setColor(entry.getValue());
diff --git a/src/org/broad/igv/ui/panel/AttributeHeaderPanel.java b/src/org/broad/igv/ui/panel/AttributeHeaderPanel.java
index f692580..6fcfe6e 100644
--- a/src/org/broad/igv/ui/panel/AttributeHeaderPanel.java
+++ b/src/org/broad/igv/ui/panel/AttributeHeaderPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -31,19 +31,21 @@ package org.broad.igv.ui.panel;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.broad.igv.PreferenceManager;
-import org.broad.igv.track.TrackManager;
+import org.broad.igv.track.AttributeManager;
 import org.broad.igv.ui.FontManager;
-import org.broad.igv.ui.GuiUtilities;
 import org.broad.igv.ui.IGVMainFrame;
 import static org.broad.igv.ui.IGVMainFrame.getInstance;
 import org.broad.igv.ui.util.Packable;
+import org.broad.igv.ui.util.UIUtilities;
 
 import javax.swing.*;
 import javax.swing.event.MouseInputAdapter;
 import java.awt.*;
 import java.awt.event.MouseEvent;
 import java.awt.geom.AffineTransform;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * @author jrobinso
@@ -76,9 +78,11 @@ public class AttributeHeaderPanel extends JPanel implements Packable {
         graphics2.fillRect(0, 0, getWidth(), getHeight());
         graphics2.setColor(Color.BLACK);
 
-        final List<String> keys = getInstance().getDisplayableAttributes();
+        final List<String> keys = AttributeManager.getInstance().getAttributeKeys();
+        Set<String> hiddenKeys = AttributeManager.getInstance().getHiddenAttributes();
+        keys.removeAll(hiddenKeys);
 
-        // Divide the remaining space to get column widths
+         // Divide the remaining space to get column widths
         int columnWidth = getAttributeColumnWidth();
 
         // Create font and font size
@@ -100,8 +104,7 @@ public class AttributeHeaderPanel extends JPanel implements Packable {
             }
 
             // Change the origin for the text
-            AffineTransform transform = AffineTransform.getTranslateInstance(0,
-                    size.height - COLUMN_BORDER_WIDTH);
+            AffineTransform transform = AffineTransform.getTranslateInstance(0, size.height - COLUMN_BORDER_WIDTH);
             graphics2.transform(transform);
 
             // Now rotate text counter-clockwise 90 degrees
@@ -121,7 +124,7 @@ public class AttributeHeaderPanel extends JPanel implements Packable {
             }
         }
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
 
@@ -171,11 +174,12 @@ public class AttributeHeaderPanel extends JPanel implements Packable {
         if (!PreferenceManager.getInstance().getShowAttributeView()) {
             return 0;
         }
+        Set<String> allAttributes = new HashSet(AttributeManager.getInstance().getAttributeKeys());
+        Set<String> hiddenAttributes = AttributeManager.getInstance().getHiddenAttributes();
 
-        List<String> attributeKeys = getInstance().getDisplayableAttributes();
-        int numberOfAttributes = attributeKeys.size();
-        int packWidth = (numberOfAttributes) * (ATTRIBUTE_COLUMN_WIDTH + COLUMN_BORDER_WIDTH)
-                + COLUMN_BORDER_WIDTH;
+        allAttributes.removeAll(hiddenAttributes);
+        int numberOfAttributes = allAttributes.size();
+        int packWidth = (numberOfAttributes) * (ATTRIBUTE_COLUMN_WIDTH + COLUMN_BORDER_WIDTH) + COLUMN_BORDER_WIDTH;
         return packWidth;
     }
 
@@ -275,8 +279,7 @@ public class AttributeHeaderPanel extends JPanel implements Packable {
 
         if (selectedSortKeys != null) {
 
-            TrackManager.getInstance().sortAllTracksByAttributes(selectedSortKeys, isSortAscending);
-            IGVMainFrame.getInstance().fireViewChangedEvent();
+            IGVMainFrame.getInstance().getTrackManager().sortAllTracksByAttributes(selectedSortKeys, isSortAscending);
             IGVMainFrame.getInstance().getContentPane().repaint();
         }
     }
diff --git a/src/org/broad/igv/ui/panel/AttributePanel.java b/src/org/broad/igv/ui/panel/AttributePanel.java
index 0af5f61..9bddc65 100644
--- a/src/org/broad/igv/ui/panel/AttributePanel.java
+++ b/src/org/broad/igv/ui/panel/AttributePanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -28,12 +28,15 @@ package org.broad.igv.ui.panel;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
 import org.broad.igv.PreferenceManager;
+import org.broad.igv.track.AttributeManager;
 import org.broad.igv.track.Track;
 import org.broad.igv.track.TrackGroup;
 import org.broad.igv.ui.IGVMainFrame;
+
 import static org.broad.igv.ui.IGVMainFrame.getInstance;
+
+import org.broad.igv.ui.UIConstants;
 import org.broad.igv.ui.util.Packable;
 
 import javax.swing.event.MouseInputAdapter;
@@ -47,29 +50,22 @@ import java.util.List;
 /**
  * @author jrobinso
  */
-public class AttributePanel extends TrackSetComponent
-        implements Packable, PropertyChangeListener {
+public class AttributePanel extends TrackPanelComponent implements Packable {
+
+    private static Logger log = Logger.getLogger(AttributePanel.class);
 
     private static Map<String, Color> colorMap = new Hashtable();
-    Logger log = Logger.getLogger(AttributePanel.class);
-    private List<String> keys;
+
 
     /**
      * Constructs ...
      */
-    public AttributePanel() {
-        this.setBorder(javax.swing.BorderFactory.createLineBorder(Color.black));
+    public AttributePanel(TrackPanel trackPanel) {
+        super(trackPanel);
+        setBorder(javax.swing.BorderFactory.createLineBorder(Color.black));
         init();
     }
 
-    /**
-     * Method description
-     *
-     * @param e
-     */
-    public void propertyChange(PropertyChangeEvent e) {
-        keys = getInstance().getDisplayableAttributes();
-    }
 
     @Override
     protected void paintComponent(Graphics g) {
@@ -77,7 +73,9 @@ public class AttributePanel extends TrackSetComponent
         super.paintComponent(g);
         removeMousableRegions();
 
-        keys = getInstance().getDisplayableAttributes();
+        List<String> keys = AttributeManager.getInstance().getAttributeKeys();
+        keys.removeAll(AttributeManager.getInstance().getHiddenAttributes());
+
         if (keys == null) {
             return;
         }
@@ -85,20 +83,20 @@ public class AttributePanel extends TrackSetComponent
         if (keys.size() > 0) {
             Rectangle visibleRect = getVisibleRect();
 
-            int width = getAttributeColumnWidth();
 
             // Get the current tracks
-            TrackSetView dataTrackView = (TrackSetView) getParent();
-            Collection<TrackGroup> groups = dataTrackView.getGroups();
+            TrackPanel trackPanel = (TrackPanel) getParent();
+            Collection<TrackGroup> groups = trackPanel.getGroups();
 
 
             if (!groups.isEmpty()) {
 
+                int attributeColumnWidth = getAttributeColumnWidth();
                 final Graphics2D graphics2D = (Graphics2D) g.create();
                 graphics2D.setColor(Color.BLACK);
 
                 final Graphics2D greyGraphics = (Graphics2D) g.create();
-                greyGraphics.setColor(IGVConstants.VERY_LIGHT_GRAY);
+                greyGraphics.setColor(UIConstants.VERY_LIGHT_GRAY);
 
                 int regionX = 3;
                 int regionY = 0;
@@ -112,8 +110,8 @@ public class AttributePanel extends TrackSetComponent
 
                     if (group.isVisible()) {
                         if (groups.size() > 1) {
-                            greyGraphics.fillRect(0, regionY + 1, width, IGVConstants.groupGap - 1);
-                            regionY += IGVConstants.groupGap;
+                            greyGraphics.fillRect(0, regionY + 1, getWidth(), UIConstants.groupGap - 1);
+                            regionY += UIConstants.groupGap;
                         }
 
 
@@ -130,8 +128,7 @@ public class AttributePanel extends TrackSetComponent
 
                             if (track.isVisible()) {
                                 if (regionY + trackHeight >= visibleRect.y) {
-                                    regionY = draw(track, regionX, regionY, width,
-                                            track.getHeight(), graphics2D);
+                                    regionY = draw(keys, track, regionX, regionY, attributeColumnWidth, track.getHeight(), graphics2D);
                                 } else {
                                     regionY += trackHeight;
                                 }
@@ -148,7 +145,12 @@ public class AttributePanel extends TrackSetComponent
     }
 
     private Color getColor(String attKey, String attValue) {
-        String key = attKey + "_" + attValue;
+
+        if (attValue == null || attValue.length() == 0) {
+            return Color.white;
+        }
+
+        String key = (attKey + "_" + attValue).toLowerCase();
         Color c = colorMap.get(key);
         if (c == null) {
 
@@ -182,7 +184,7 @@ public class AttributePanel extends TrackSetComponent
         return Color.getHSBColor(hue, sat, bri);
     }
 
-    private int draw(Track track, int trackX, int trackY, int trackWidth, int trackHeight,
+    private int draw(List<String> keys, Track track, int trackX, int trackY, int trackWidth, int trackHeight,
                      Graphics2D graphics) {
 
         for (String key : keys) {
@@ -240,10 +242,10 @@ public class AttributePanel extends TrackSetComponent
         Collection<Track> selectedTracks = getSelectedTracks();
         int selectedTrackCount = selectedTracks.size();
         if (selectedTrackCount == 1) {
-            return selectedTracks.iterator().next().getDisplayName();
+            return selectedTracks.iterator().next().getName();
         } else {
             String keyValue = "";
-            for (MouseableRegion region : this.getMouseableRegions()) {
+            for (MouseableRegion region : this.getTrackRegions()) {
                 if (region.containsPoint(x, y)) {
                     keyValue = region.getText();
                 }
@@ -261,7 +263,7 @@ public class AttributePanel extends TrackSetComponent
      */
     public String getMouseDoc(int x, int y) {
 
-        List<MouseableRegion> mouseRegions = getMouseableRegions();
+        List<MouseableRegion> mouseRegions = getTrackRegions();
 
         for (MouseableRegion mr : mouseRegions) {
             if (mr.containsPoint(x, y)) {
@@ -281,6 +283,7 @@ public class AttributePanel extends TrackSetComponent
     }
 
     // Packable interface
+
     /**
      * Method description
      *
@@ -292,6 +295,7 @@ public class AttributePanel extends TrackSetComponent
 
 
     // Code below is used to pack the attribute pane
+
     /**
      * Method description
      */
@@ -313,9 +317,12 @@ public class AttributePanel extends TrackSetComponent
             return 0;
         }
 
-        List<String> attributeKeys = getInstance().getDisplayableAttributes();
+        HashSet<String> attributeKeys = new HashSet(AttributeManager.getInstance().getAttributeKeys());
+        attributeKeys.removeAll(AttributeManager.getInstance().getHiddenAttributes());
+
         int attributeCount = attributeKeys.size();
-        int packWidth = (attributeCount) * (AttributeHeaderPanel.ATTRIBUTE_COLUMN_WIDTH + AttributeHeaderPanel.COLUMN_BORDER_WIDTH) + AttributeHeaderPanel.COLUMN_BORDER_WIDTH;
+        int packWidth = (attributeCount) * (AttributeHeaderPanel.ATTRIBUTE_COLUMN_WIDTH +
+                AttributeHeaderPanel.COLUMN_BORDER_WIDTH) + AttributeHeaderPanel.COLUMN_BORDER_WIDTH;
         return packWidth;
     }
 
@@ -362,7 +369,6 @@ public class AttributePanel extends TrackSetComponent
                 openPopupMenu(e);
 
             }
-
             IGVMainFrame.getInstance().repaintNamePanels();
         }
 
@@ -371,7 +377,7 @@ public class AttributePanel extends TrackSetComponent
             // Note: clearing after this operation is "non standard", at least on the mac
             if (e.isPopupTrigger()) {
                 openPopupMenu(e);
-
+                clearTrackSelections();
             }
 
         }
diff --git a/src/org/broad/igv/ui/panel/CachedDataPanelPainter.java b/src/org/broad/igv/ui/panel/CachedDataPanelPainter.java
deleted file mode 100644
index 3b6d369..0000000
--- a/src/org/broad/igv/ui/panel/CachedDataPanelPainter.java
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.ui.panel;
-
-import org.apache.log4j.Logger;
-import org.broad.igv.track.TrackGroup;
-import org.broad.igv.ui.ViewContext;
-import org.broad.igv.ui.WaitCursorManager;
-import org.broad.igv.ui.WaitCursorManager.CursorToken;
-
-import java.awt.*;
-import java.util.Collection;
-import java.util.Stack;
-import java.util.Timer;
-import java.util.TimerTask;
-import java.util.concurrent.Callable;
-
-/**
- * @author eflakes
- */
-public class CachedDataPanelPainter implements CachedIGVDataPanelPainter {
-
-    private double requestCount;
-    private double processedCount;
-    private double discardedCount;
-    private boolean isDebugEnabled = false;
-    private Timer debugTimer = new Timer("CachedDataPanelPainter Debug Thread");
-    private TimerTask debuggingTask = new TimerTask() {
-
-        public void run() {
-            printDebuggingStatistics();
-        }
-    };
-    private final int MAX_TILE_STACK_SIZE = 3;
-    private static Logger logger = Logger.getLogger(CachedDataPanelPainter.class);
-    // Class to manage cached images.
-    private DataPanelImageManager imageManager = new DataPanelImageManager();
-    /**
-     * Keep track of image tiles currently being built.
-     * TODO move to image manager
-     */
-    private final Stack<Integer> tileStack = new Stack();
-
-    /**
-     * Do a cached image paint.
-     *
-     * @param dataPanel
-     * @param groups
-     * @param graphics2D
-     */
-    final public void paint(final DataPanel dataPanel,
-                            final Collection<TrackGroup> groups, Graphics2D graphics2D) {
-
-        final ViewContext viewContext = dataPanel.getViewContext();
-
-        final String chr = viewContext.getChrName();
-        final double scale = viewContext.getScale();
-        final double startLocation = viewContext.getOrigin();
-        final double endLocation = (startLocation + dataPanel.getWidth() * scale);
-        final int zoomLevel = viewContext.getZoom();
-        final double scaledImageWidth = DataPanelImageManager.IMAGE_WIDTH * scale;
-
-        // Compute the start and end "tile" (image index) 
-        // to cover the requested region
-        final int startTile = (int) Math.max(0, (startLocation / scaledImageWidth));
-        final int endTile = (int) (endLocation / scaledImageWidth);
-
-        // Loop through tiles
-        for (int tileNumber = startTile; tileNumber <= endTile; tileNumber++) {
-
-            final int tile = tileNumber;
-
-            // Remove duplicate before adding the latest requested tile
-            synchronized (tileStack) {
-                int index = tileStack.indexOf(tileNumber);
-                if (index != -1) tileStack.remove(index);
-            }
-
-            // Fetch image for this chromosome, zoomlevel, and tile.
-            // If found draw immediately
-            String key = dataPanel.generateTileKey(chr, tile, zoomLevel);
-            CachedImageWrapper imageWrapper = imageManager.getCachedImageWrapper(key);
-            if (imageWrapper != null) {
-                int xOffset = (int) ((imageWrapper.getOrigin() - startLocation) / scale);
-
-                // TODO -- something like...
-                // int yOffset = imageWrapper.getYPosition() - scrollYPoistion,   or maybe vice versa
-                int yOffset = 0;
-
-                graphics2D.drawImage(imageWrapper.getImage(), xOffset, yOffset, null);
-            } else {
-
-                // Write message on canvas indicating image is loading TODO -- center the string
-                final int tileOrigin = (int) (tile * scaledImageWidth);
-                int leftPixelPos = Math.max(0, viewContext.getPixelPosition(tileOrigin) + 10);
-                for (int w = leftPixelPos; w < dataPanel.getWidth(); w += 250) {
-                    graphics2D.drawString(" loading ...", w, dataPanel.getHeight() / 2);
-                }
-
-                // Image was not found and must be built. It takes time to build 
-                // an image, use a new thread to prevent blocking swing thread.
-                tileStack.push(tile);
-                ++requestCount;
-
-                //No work to do if the stack is empty
-                if (!tileStack.isEmpty()) {
-                    Callable<Integer> imageBuilderCallable =
-                            createCallable(dataPanel, groups, zoomLevel, chr, scale, tileStack);
-                    imageManager.submit(imageBuilderCallable);
-                }
-            }
-
-        }
-    }
-
-    /**
-     * Create the callable thread object.
-     *
-     * @param dataPanel
-     * @param groups
-     * @param zoomLevel
-     * @param chromosome
-     * @param scale
-     * @return
-     */
-    private Callable<Integer> createCallable(final DataPanel dataPanel,
-                                             final Collection<TrackGroup> groups, final int zoomLevel,
-                                             final String chromosome, final double scale,
-                                             final Stack<Integer> tilesToLoadStack) {
-
-        // Now every Callable callable shares the resposibility of cleaning 
-        // up the same LIFO queue
-        final Callable<Integer> imageBuilderCallable = new Callable() {
-
-            final public Integer call() {
-
-                CursorToken token = WaitCursorManager.showWaitCursor();
-                try {
-
-                    // Process all requested images
-                    // TODO get this to clear out all but the top 3? tile in 
-                    // queue (performance enhancement)
-                    while (true) {
-
-                        int t;
-                        synchronized (tilesToLoadStack) {
-                            if (tilesToLoadStack.isEmpty()) {
-                                break;
-                            } // All images processed
-                            t = tilesToLoadStack.pop();
-                        }
-
-                        doImageBuild(t);
-
-                        ++processedCount;
-
-
-                        if (!dataPanel.isPaintingTile()) {
-                            dataPanel.repaint();
-                        }
-
-                        // If the stack has more items on it than we want
-                        // we should trim it back by removing the oldest 
-                        // draw requests which are not really necessary
-                        synchronized (tilesToLoadStack) {
-
-                            int stackSize = tilesToLoadStack.size();
-                            if (stackSize > MAX_TILE_STACK_SIZE) {
-
-                                int indexOfLastStackItem = stackSize - 1;
-                                while (tilesToLoadStack.size() > MAX_TILE_STACK_SIZE) {
-                                    tilesToLoadStack.remove(indexOfLastStackItem--);
-                                    ++discardedCount;
-                                }
-                            }
-                        }
-                    }
-
-                    return 1;
-
-                } catch (Exception ex) {
-                    logger.error("Error in repaint call", ex);
-                    return -1;
-                } finally {
-                    WaitCursorManager.removeWaitCursor(token);
-
-                }
-            }
-
-            final public void doImageBuild(int tile) {
-
-                String key = dataPanel.generateTileKey(chromosome, tile, zoomLevel);
-                double tileOrigin = (tile * scale) * DataPanelImageManager.IMAGE_WIDTH;
-
-                Color background = dataPanel.getBackground();
-
-                // TODO,  input the yPosition for this tile 
-                int yPosition = 0;
-                // TODO, something like height = Math.min(dataPanel.getHeight(), DataPanelImageManager.IMAGE_HEIGHT);
-                int height = dataPanel.getHeight();
-
-                imageManager.buildImage(groups, key, yPosition, tileOrigin, scale,
-                        height, background);
-
-            }
-        };
-
-        return imageBuilderCallable;
-    }
-
-    public void setDebuggingEnabled(boolean enabled) {
-
-        isDebugEnabled = enabled;
-        if (isDebugEnabled) {
-            debugTimer.schedule(debuggingTask, 10000, 3000);
-        } else {
-            debuggingTask.cancel();
-        }
-    }
-
-    public void clearImageCache() {
-        imageManager.clearCache();
-    }
-
-    synchronized public void printDebuggingStatistics() {
-
-        System.out.println("\n\nGetting statistics");
-        System.out.println("\trequestCount: " + requestCount);
-        System.out.println("\tprocessedCount: " + processedCount);
-        System.out.println("\tdiscardedCount: " + discardedCount);
-    }
-}
diff --git a/src/org/broad/igv/ui/panel/CachedIGVDataPanelPainter.java b/src/org/broad/igv/ui/panel/CachedIGVDataPanelPainter.java
deleted file mode 100644
index 758b91d..0000000
--- a/src/org/broad/igv/ui/panel/CachedIGVDataPanelPainter.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-package org.broad.igv.ui.panel;
-
-import org.broad.igv.track.TrackGroup;
-
-import java.awt.*;
-import java.util.Collection;
-
-/**
- * @author eflakes
- */
-public interface CachedIGVDataPanelPainter {
-
-    public void paint(final DataPanel dataPanel, final Collection<TrackGroup> groups, Graphics2D graphics2D);
-
-    public void clearImageCache();
-
-    public void printDebuggingStatistics();
-
-    public void setDebuggingEnabled(boolean enabled);
-}
diff --git a/src/org/broad/igv/ui/panel/CachedImageWrapper.java b/src/org/broad/igv/ui/panel/CachedImageWrapper.java
deleted file mode 100644
index 1436dd4..0000000
--- a/src/org/broad/igv/ui/panel/CachedImageWrapper.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.ui.panel;
-
-import org.apache.log4j.Logger;
-
-import javax.imageio.IIOImage;
-import javax.imageio.ImageIO;
-import javax.imageio.ImageWriteParam;
-import javax.imageio.ImageWriter;
-import javax.imageio.stream.ImageOutputStream;
-import java.awt.*;
-import java.awt.image.BufferedImage;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.Iterator;
-
-/**
- * @author jrobinso
- */
-public class CachedImageWrapper {
-
-    private static Logger log = Logger.getLogger(CachedImageWrapper.class);
-
-    /**
-     * Set to true to enable image compression.  It was hoped this would increase
-     * perfomance by decreasing the amount of memory required by large images.
-     * This did not pan out,  but the option is retained and defaulted to "false".
-     */
-    private static boolean useCompression = false;
-    /**
-     * Origin in base pairs
-     */
-    private double origin;
-    /**
-     * Scale in bp / pixel
-     */
-    private double scale;
-
-    /**
-     * Vertical offset for this tile in pixels
-     */
-    private int yPosition;
-
-    /**
-     * The image
-     */
-    private BufferedImage image;
-    private byte[] compressedBytes;
-
-    public CachedImageWrapper(int yOffset, double origin, double scale, BufferedImage image) {
-        this.yPosition = yOffset;
-        this.origin = origin;
-        this.scale = scale;
-        if (useCompression) {
-            compressImage(image, 0.5f);
-        } else {
-            this.image = image;
-        }
-    }
-
-    public int getYPosition() {
-        return yPosition;
-
-    }
-
-    public double getOrigin() {
-        return origin;
-    }
-
-    public double getScale() {
-        return scale;
-    }
-
-    public Image getImage() {
-        if (image == null) {
-            return decompressImage();
-        }
-        return image;
-    }
-
-    private void compressImage(BufferedImage image, float quality) {
-
-        try {
-
-            // Get a ImageWriter for jpeg format.
-            Iterator<ImageWriter> writers = ImageIO.getImageWritersBySuffix("jpeg");
-            if (!writers.hasNext()) {
-                throw new IllegalStateException("No writers found");
-            }
-            ImageWriter writer = (ImageWriter) writers.next();
-            // Create the ImageWriteParam to compress the image.
-            ImageWriteParam param = writer.getDefaultWriteParam();
-            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
-            param.setCompressionQuality(quality);
-            // The output will be a ByteArrayOutputStream (in memory)
-            ByteArrayOutputStream bos = new ByteArrayOutputStream(32768);
-            ImageOutputStream ios = ImageIO.createImageOutputStream(bos);
-            writer.setOutput(ios);
-            writer.write(null, new IIOImage(image, null, null), param);
-            ios.flush(); // otherwise the buffer size will be zero!
-
-            compressedBytes = bos.toByteArray();
-            this.image = null;
-
-            System.out.println("Image byte array size = " + compressedBytes.length);
-
-        } catch (IOException iOException) {
-            log.error("Error compressing image", iOException);
-            this.image = image;
-        }
-
-    }
-
-    private Image decompressImage() {
-        if (compressedBytes == null) {
-            return null;
-        }
-        try {
-            ByteArrayInputStream in = new ByteArrayInputStream(compressedBytes);
-            return ImageIO.read(in);
-
-        } catch (IOException iOException) {
-            log.error("Error decompressing image");
-            return null;
-        }
-
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        super.finalize();
-        //System.out.println("Finalizing cached image");
-    }
-
-
-}
diff --git a/src/org/broad/igv/ui/panel/CytobandPanel.java b/src/org/broad/igv/ui/panel/CytobandPanel.java
index 3db1440..812e49a 100644
--- a/src/org/broad/igv/ui/panel/CytobandPanel.java
+++ b/src/org/broad/igv/ui/panel/CytobandPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -29,11 +29,15 @@ package org.broad.igv.ui.panel;
 
 //~--- non-JDK imports --------------------------------------------------------
 
-import org.broad.igv.IGVConstants;
+import org.broad.igv.Globals;
 import org.broad.igv.feature.Chromosome;
 import org.broad.igv.feature.Cytoband;
 import org.broad.igv.renderer.CytobandRenderer;
-import org.broad.igv.ui.*;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.ui.FontManager;
+import org.broad.igv.ui.WaitCursorManager;
+import org.broad.igv.util.LongRunningTask;
+import org.broad.igv.util.NamedRunnable;
 
 import javax.swing.*;
 import javax.swing.event.MouseInputAdapter;
@@ -77,7 +81,7 @@ public class CytobandPanel extends JPanel {
 
         super.paintComponent(g);
 
-        if (getViewContext().getChrName().equals(IGVConstants.CHR_ALL)) {
+        if (getViewContext().getChrName().equals(Globals.CHR_ALL)) {
             Graphics g2 = g.create();
             g2.setFont(FontManager.getScalableFont(Font.ITALIC, 12));
             String text = "Whole genome view.  To jump to a chromosome click on its label.";
@@ -85,25 +89,24 @@ public class CytobandPanel extends JPanel {
             return;
         }
 
-        int dataPanelWidth = getViewContext().getDataPanelWidth();
+        int dataPanelWidth = getWidth(); //getViewContext().getDataPanelWidth();
         Rectangle cytoRect = new Rectangle(0, 10, dataPanelWidth, bandHeight);
 
         Chromosome chromosome = getViewContext().getChromosome();
-        if(chromosome == null) {
+        if (chromosome == null) {
             return;
         }
         java.util.List<Cytoband> cytobands = chromosome.getCytobands();
-        if(cytobands == null) {
+        if (cytobands == null) {
             return;
         }
-        (new CytobandRenderer()).draw(cytobands,  g, cytoRect);
+        (new CytobandRenderer()).draw(cytobands, g, cytoRect);
 
         int chromosomeLength = getViewContext().getChromosomeLength();
         cytobandScale = ((double) chromosomeLength) / dataPanelWidth;
 
-        // The test is true if we are zoomed in (that is, if the virtual image size is greater
-        // than the viewport width).
-        if (getViewContext().getMaxPixel() > dataPanelWidth) {
+        // The test is true if we are zoomed in 
+        if (getViewContext().getZoom() > 0) {
 
             double scale = getViewContext().getScale();
 
@@ -144,19 +147,20 @@ public class CytobandPanel extends JPanel {
             public void mouseClicked(MouseEvent e) {
                 final int mouseX = e.getX();
                 final int clickCount = e.getClickCount();
-                LongRunningTask.submit(new Runnable() {
-
-                    public void run() {
-                        double newLocation = cytobandScale * mouseX;
-                        if (clickCount > 1) {
-                            final int newZoom = getViewContext().getZoom() + 1;
-                            getViewContext().zoomTo(newZoom, newLocation);
-                        } else {
-                            getViewContext().centerOnLocation(newLocation);
-                        }
-
+                WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
+                try {
+
+                    double newLocation = cytobandScale * mouseX;
+                    if (clickCount > 1) {
+                        final int newZoom = getViewContext().getZoom() + 1;
+                        getViewContext().zoomTo(newZoom, newLocation);
+                    } else {
+                        getViewContext().centerOnLocation(newLocation);
                     }
-                });
+
+                } finally {
+                    WaitCursorManager.removeWaitCursor(token);
+                }
             }
 
             @Override
@@ -167,12 +171,12 @@ public class CytobandPanel extends JPanel {
             @Override
             public void mouseReleased(MouseEvent e) {
                 if (isDragging) {
-                    LongRunningTask.submit(new Runnable() {
-
-                        public void run() {
-                            getViewContext().setOrigin(viewOrigin);
-                        }
-                    });
+                    WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
+                    try {
+                        getViewContext().setOrigin(viewOrigin);
+                    } finally {
+                        WaitCursorManager.removeWaitCursor(token);
+                    }
                 }
                 isDragging = false;
             }
@@ -192,7 +196,7 @@ public class CytobandPanel extends JPanel {
                  * double scaledDataPanelWidth = dataPanelWidth * scale;
                  * int span = (int) (scaledDataPanelWidth / cytobandScale);
                  */
-                int w = getViewContext().getDataPanelWidth();
+                int w = getWidth();
                 double scale = getViewContext().getScale();
                 int x = (int) Math.max(0, Math.min(e.getX(), w * (cytobandScale - scale)));
                 int delta = x - lastMousePressX;
@@ -232,7 +236,8 @@ public class CytobandPanel extends JPanel {
     }
 
     // TODO remove reference to IGVMainFrame.theInstance
+
     private ViewContext getViewContext() {
-        return IGVModel.getInstance().getViewContext();
+        return ViewContext.getInstance();
     }
 }
diff --git a/src/org/broad/igv/ui/panel/DataPanel.java b/src/org/broad/igv/ui/panel/DataPanel.java
index 1a35282..55b619a 100644
--- a/src/org/broad/igv/ui/panel/DataPanel.java
+++ b/src/org/broad/igv/ui/panel/DataPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -28,17 +28,18 @@ package org.broad.igv.ui.panel;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
+import org.broad.igv.PreferenceManager;
 import org.broad.igv.exceptions.DataLoadException;
 import org.broad.igv.renderer.DataRange;
-import org.broad.igv.renderer.XYPlotRenderer;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.track.RenderContext;
 import org.broad.igv.track.Track;
 import org.broad.igv.track.TrackGroup;
-import org.broad.igv.track.TrackManager;
 import org.broad.igv.ui.*;
+import org.broad.igv.ui.util.MessageUtils;
 import org.broad.igv.ui.util.Packable;
 import org.broad.igv.ui.util.UIUtilities;
+import org.broad.igv.util.ResourceLocator;
 
 import javax.swing.*;
 import javax.swing.event.MouseInputAdapter;
@@ -51,82 +52,49 @@ import java.awt.event.KeyEvent;
 import java.awt.event.MouseEvent;
 import java.io.File;
 import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
+import java.util.*;
 import java.util.List;
 
 /**
  * @author jrobinso
  */
-public class DataPanel extends TrackSetComponent {
+public class DataPanel extends TrackPanelComponent {
 
     private static Logger log = Logger.getLogger(DataPanel.class);
 
     // TODO move this to some central place
-    final static private boolean IS_MAC =
-            System.getProperty("os.name").toLowerCase().startsWith("mac");
-    /**
-     * Parameter to record current width.  The various resize methods get called often when no
-     * actual resize has occurred,  this variable is used to determine if the width has
-     * really changed.
-     */
-    private int currentWidth = -1;
-    /**
-     * Used to temporarily disable image caching, for example to create a PDF screenshot.
-     */
-    private boolean suspendImageCaching = false;
-    private CachedIGVDataPanelPainter cachedDataPanelPainter;
-    /**
-     * Keep track of image tiles currently being built.
-     * TODO move to image manager
-     */
+    final static private boolean IS_MAC = System.getProperty("os.name").toLowerCase().startsWith("mac");
 
-    // HashSet<String> tilesInProgress = new HashSet<String>();
-    IGVTool currentTool;
-    IGVTool previousTool;
-    IGVTool defaultTool;
 
-    /**
-     * @return the panAndZoomTool
-     */
-    public PanAndZoomTool getPanAndZoomTool() {
-        return panAndZoomTool;
-    }
-
-    final static private class Position {
-
-        int x;
-        int y;
-    }
-
-    private Position tooltipTextPosition;
+    private PanAndZoomTool panAndZoomTool;
+    private IGVTool currentTool;
+    private IGVTool previousTool;
+    private IGVTool defaultTool;
+    private Point tooltipTextPosition;
 
     /**
      * Constructs ...
      */
-    public DataPanel() {
-        setCachedDataPanelPainter(new CachedDataPanelPainter());
+    public DataPanel(TrackPanel trackPanel) {
+        super(trackPanel);
         init();
         setFocusable(true);
         setAutoscrolls(true);
         setToolTipText("Data panel");
 
-        DropTarget target = new DropTarget(this,
-                new FileDropTargetListener(this));
+        DropTarget target = new DropTarget(this, new FileDropTargetListener(trackPanel));
         setDropTarget(target);
         target.setActive(true);
     }
 
-    public void setCachedDataPanelPainter(CachedIGVDataPanelPainter painter) {
-
-        if (cachedDataPanelPainter != null) {
-            cachedDataPanelPainter.setDebuggingEnabled(false);
-        }
-
-        cachedDataPanelPainter = painter;
+    /**
+     * @return the panAndZoomTool
+     */
+    public PanAndZoomTool getPanAndZoomTool() {
+        return panAndZoomTool;
     }
 
+
     /**
      * @return
      */
@@ -145,7 +113,7 @@ public class DataPanel extends TrackSetComponent {
      */
     public void setCurrentTool(final IGVTool tool) {
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
 
@@ -182,6 +150,7 @@ public class DataPanel extends TrackSetComponent {
     }
 
     // final Stack<Integer> tilesToLoadStack = new Stack();
+
     /**
      * Method description
      *
@@ -195,6 +164,7 @@ public class DataPanel extends TrackSetComponent {
         //}
         super.paintComponent(g);
 
+        RenderContext context = null;
         try {
 
             Graphics2D graphics2D = (Graphics2D) g.create();
@@ -203,11 +173,10 @@ public class DataPanel extends TrackSetComponent {
             double startLocation = getViewContext().getOrigin();
             double endLocation = (startLocation + getWidth() * scale + 1);
             int zoom = getViewContext().getZoom();
+            String genomeId = getViewContext().getGenomeId();
 
-            RenderContext context = new RenderContext(chromosomeName,
-                    startLocation, endLocation,
-                    zoom, getViewContext().getOrigin(), scale, getVisibleRect(),
-                    graphics2D);
+            context = new RenderContext(genomeId, chromosomeName, startLocation, endLocation,
+                    zoom, scale, getVisibleRect(), graphics2D);
 
             if (IS_MAC) {
                 this.applyMacPerformanceHints((Graphics2D) g);
@@ -215,7 +184,7 @@ public class DataPanel extends TrackSetComponent {
 
 
             graphics2D.setBackground(getBackground());
-            graphics2D.clearRect(0, 0, getWidth(), getHeight());
+            //graphics2D.clearRect(0, 0, getWidth(), getHeight());
 
             final Collection<TrackGroup> groups = new ArrayList(getTrackGroups());
 
@@ -228,6 +197,7 @@ public class DataPanel extends TrackSetComponent {
                 }
             }
             if (!hasTracks) {
+                removeMousableRegions();
                 return;
             }
 
@@ -249,15 +219,13 @@ public class DataPanel extends TrackSetComponent {
 
                 TrackGroup group = groupIter.next();
                 if (groups.size() > 1) {
-                    trackY += IGVConstants.groupGap;
+                    trackY += UIConstants.groupGap;
                 }
 
                 // Draw borders
                 for (Track track : group.getTracks()) {
                     if (track.isVisible()) {
-                        Rectangle rect = new Rectangle(trackX, trackY,
-                                getWidth(),
-                                track.getHeight());
+                        Rectangle rect = new Rectangle(trackX, trackY, getWidth(), track.getHeight());
                         track.renderAxis(context, rect);
                         trackY += track.getHeight();
                     }
@@ -273,19 +241,17 @@ public class DataPanel extends TrackSetComponent {
                     graphics2D.drawLine(start, 0, start, getHeight());
                 }
             }
-        } finally {
 
-            ViewContext viewContext = getViewContext();
 
-            // Draw all stored regions of interest
-            if (IGVMainFrame.getInstance().isShowRegionsOfInterestBarsOn() || (currentTool instanceof RegionOfInterestTool)) {
-                drawRegionsOfInterest(g, getHeight(), viewContext);
+            if (IGVMainFrame.getInstance().isShowRegionsOfInterestBarsOn()) {
+                drawAllRegions(g);
             }
 
-            //if (log.isDebugEnabled()) {
-            //    log.debug("Finish paintComponent " + getName());
-            //}
+        } finally {
 
+            if (context != null) {
+                context.dispose();
+            }
         }
     }
 
@@ -293,43 +259,24 @@ public class DataPanel extends TrackSetComponent {
      * Method description
      *
      * @param g
-     * @param height
-     * @param viewContext
      */
-    public void drawRegionsOfInterest(final Graphics g, int height,
-                                      ViewContext viewContext) {
+    public void drawAllRegions(final Graphics g) {
 
         Collection<RegionOfInterest> regions =
-                IGVModel.getInstance().getRegionsOfInterest(
-                        getViewContext().getChrName());
+                IGVMainFrame.getInstance().getSession().getRegionsOfInterest(getViewContext().getChrName());
 
         if ((regions == null) || regions.isEmpty()) {
             return;
         }
 
+        boolean drawBars = PreferenceManager.getInstance().isShowRegionBars();
         Graphics2D graphics2D = (Graphics2D) g.create();
         try {
 
             for (RegionOfInterest regionOfInterest : regions) {
-
-                Integer regionStart = regionOfInterest.getStart();
-                if (regionStart == null) {
-
-                    // skip - null starts are bad regions of interest
-                    continue;
+                if (drawBars || regionOfInterest == IGVMainFrame.getInstance().getSelectedRegion()) {
+                    drawRegion(graphics2D, regionOfInterest);
                 }
-
-                Integer regionEnd = regionOfInterest.getEnd();
-                if (regionEnd == null) {
-                    regionEnd = regionStart;
-                }
-                int start = viewContext.getPixelPosition(regionStart);
-                int end = viewContext.getPixelPosition(regionEnd);
-
-                // Set foreground color of boundaries
-                graphics2D.setColor(regionOfInterest.getForegroundColor());
-                graphics2D.drawLine(start, 0, start, height);
-                graphics2D.drawLine(end, 0, end, height);
             }
         } finally {
             if (graphics2D != null) {
@@ -338,6 +285,28 @@ public class DataPanel extends TrackSetComponent {
         }
     }
 
+    private boolean drawRegion(Graphics2D graphics2D, RegionOfInterest regionOfInterest) {
+        Integer regionStart = regionOfInterest.getStart();
+        if (regionStart == null) {
+            return true;
+        }
+
+        Integer regionEnd = regionOfInterest.getEnd();
+        if (regionEnd == null) {
+            regionEnd = regionStart;
+        }
+        ViewContext viewContext = getViewContext();
+        int start = viewContext.getPixelPosition(regionStart);
+        int end = viewContext.getPixelPosition(regionEnd);
+
+        // Set foreground color of boundaries
+        int height = getHeight();
+        graphics2D.setColor(regionOfInterest.getForegroundColor());
+        graphics2D.drawLine(start, 0, start, height);
+        graphics2D.drawLine(end, 0, end, height);
+        return false;
+    }
+
     protected String generateTileKey(final String chr, int t,
                                      final int zoomLevel) {
 
@@ -365,7 +334,7 @@ public class DataPanel extends TrackSetComponent {
             TrackGroup group = groupIter.next();
 
             if (groups.size() > 1) {
-                trackY += IGVConstants.groupGap;
+                trackY += UIConstants.groupGap;
             }
             for (Track track : group.getTracks()) {
 
@@ -393,25 +362,17 @@ public class DataPanel extends TrackSetComponent {
      */
     public void doResize() {
 
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
 
                 // Get the view that holds the track name, attribute and data panels
-                final TrackSetView trackView = (TrackSetView) getParent();
+                final TrackPanel trackView = (TrackPanel) getParent();
 
                 if (trackView == null) {
                     return;
                 }
 
-                // Only need to clear the cache when the view is stretched (low resolution levels)
-                if (getWidth() != currentWidth) {
-                    if (getViewContext().isStretched()) {
-                        clearImageCache();
-                    }
-                    currentWidth = getWidth();
-                }
-
                 final int newHeight = calculateResizeHeight();
 
                 // The height needs to be done on the swing thread.
@@ -429,7 +390,7 @@ public class DataPanel extends TrackSetComponent {
     public void debugDump(String trackName) {
 
         // Get the view that holds the track name, attribute and data panels
-        TrackSetView trackView = (TrackSetView) getParent();
+        TrackPanel trackView = (TrackPanel) getParent();
 
         if (trackView == null) {
             return;
@@ -439,9 +400,9 @@ public class DataPanel extends TrackSetComponent {
         if (trackView.hasTracks()) {
             String name = this.getTrackSetID().toString();
             System.out.println(
-                    "\n\n" + name + " Track COUNT:" + trackView.getAllTracks().size());
+                    "\n\n" + name + " Track COUNT:" + trackView.getTracks().size());
             System.out.println(
-                    "\t\t\t\t" + name + " scrollpane height     = " + trackView.getScrollPaneHeight());
+                    "\t\t\t\t" + name + " scrollpane height     = " + trackView.getScrollPane().getHeight());
             System.out.println(
                     "\t\t\t\t" + name + " viewport height       = " + trackView.getViewportHeight());
             System.out.println(
@@ -468,7 +429,7 @@ public class DataPanel extends TrackSetComponent {
      * @return
      */
     public Track getTrack(int x, int y) {
-        for (MouseableRegion mouseRegion : getMouseableRegions()) {
+        for (MouseableRegion mouseRegion : getTrackRegions()) {
             if (mouseRegion.containsPoint(x, y)) {
                 return mouseRegion.getTracks().iterator().next();
             }
@@ -491,7 +452,7 @@ public class DataPanel extends TrackSetComponent {
         popupTextBuffer.append("<html>");
 
         Track track = null;
-        List<MouseableRegion> regions = this.getMouseableRegions();
+        List<MouseableRegion> regions = this.getTrackRegions();
         for (MouseableRegion mouseRegion : regions) {
             if (mouseRegion.containsPoint(x, y)) {
                 track = mouseRegion.getTracks().iterator().next();
@@ -502,27 +463,25 @@ public class DataPanel extends TrackSetComponent {
 
                     // First see if there is an overlay track.  If there is, give
                     // it first crack
-                    List<Track> overlays = TrackManager.getInstance().getOverlayTracks(
-                            track);
+                    List<Track> overlays = IGVMainFrame.getInstance().getTrackManager().getOverlayTracks(track);
+
                     if (overlays != null) {
                         for (Track overlay : overlays) {
                             if ((overlay != track) && (overlay.getValueStringAt(
                                     getViewContext().getChrName(),
                                     displayLocation, yRelative) != null)) {
-                                popupTextBuffer.append(getPopUpText(overlay,
-                                        displayLocation,
-                                        yRelative));
+                                popupTextBuffer.append(getPopUpText(overlay, displayLocation, yRelative));
                                 popupTextBuffer.append("<br>");
                             }
                         }
                     }
 
-                    popupTextBuffer.append(getPopUpText(track, displayLocation,
-                            y));
+                    popupTextBuffer.append(getPopUpText(track, displayLocation, y));
                 }
                 break;
             }
         }
+
         popupTextBuffer.append("<br>Location: ");
         popupTextBuffer.append(locationFormatter.format((int) displayLocation));
 
@@ -531,11 +490,7 @@ public class DataPanel extends TrackSetComponent {
 
     private boolean isWaitingForToolTipText = false;
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     @Override
     final public String getToolTipText() {
 
@@ -549,13 +504,7 @@ public class DataPanel extends TrackSetComponent {
         return super.getToolTipText();
     }
 
-    /**
-     * Method description
-     *
-     * @param x
-     * @param y
-     * @return
-     */
+
     public String getPopupMenuTitle(int x, int y) {
 
         Collection<Track> tracks = getSelectedTracks();
@@ -564,7 +513,7 @@ public class DataPanel extends TrackSetComponent {
         // Title for the popup
         if (!tracks.isEmpty()) {
             for (Track track : tracks) {
-                popupTitle = track.getDisplayName();
+                popupTitle = track.getName();
                 break;
             }
         }
@@ -583,48 +532,18 @@ public class DataPanel extends TrackSetComponent {
 
         DataRange axisDefinition = track.getDataRange();
         StringBuffer buf = new StringBuffer();
-        buf.append(track.getDisplayName() + "<br>");
-        if ((axisDefinition != null) && (track.getRenderer() instanceof XYPlotRenderer)) {
-            buf.append(
-                    "Data scale: " + axisDefinition.getMinimum() + " - " + axisDefinition.getMaximum() + "<br>");
-        }
-
-        String value = track.getValueStringAt(getViewContext().getChrName(),
-                location, y);
+        String value = track.getValueStringAt(getViewContext().getChrName(), location, y);
         if (value != null) {
             buf.append(value);
         }
         return buf.toString();
     }
 
-    /**
-     * Refresh all track data.
-     * TODO -- do this in a SwingWorker and eliminate "invokeOnEventThread" call.
-     */
-    private void refreshTrackData() {
-
-
-        LongRunningTask.submit(new Runnable() {
-
-            public void run() {
-                log.debug("Run refreshTrackData");
-
-                TrackManager.getInstance().refreshTrackData();
-                clearImageCache();
-                repaint();
-
-                log.debug("Finish refreshTrackData");
-
-            }
-        });
-
-    }
-
-    private PanAndZoomTool panAndZoomTool;
 
     // TODO -- this method and the key and mouse adapters are nearly identical
     // for the DataPanel and FeaturePanel classes.  Refractor to combine those
     // classes, or share this method in some other way.
+
     private void init() {
         panAndZoomTool = new PanAndZoomTool(this);
         setCurrentTool(getPanAndZoomTool());
@@ -648,43 +567,43 @@ public class DataPanel extends TrackSetComponent {
 
             @Override
             public void keyPressed(KeyEvent e) {
-                if (e.getKeyCode() == KeyEvent.VK_F5) {
-                    refreshTrackData();
-                } else if (e.getKeyChar() == '+' || e.getKeyCode() == KeyEvent.VK_PLUS) {
-                    LongRunningTask.submit(new Runnable() {
-
-                        public void run() {
-                            getViewContext().incrementZoom(1);
-                        }
-                    });
+                if (e.getKeyChar() == '+' || e.getKeyCode() == KeyEvent.VK_PLUS) {
+                    WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
+                    try {
+                        getViewContext().incrementZoom(1);
+                    } finally {
+                        WaitCursorManager.removeWaitCursor(token);
+                    }
                 } else if (e.getKeyChar() == '-' || e.getKeyCode() == KeyEvent.VK_PLUS) {
-                    LongRunningTask.submit(new Runnable() {
-
-                        public void run() {
-                            getViewContext().incrementZoom(-1);
-                        }
-                    });
+                    WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
+                    try {
+                        getViewContext().incrementZoom(-1);
+                    } finally {
+                        WaitCursorManager.removeWaitCursor(token);
+                    }
 
                 } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
                     getViewContext().shiftOriginPixels(5);
                 } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                     getViewContext().shiftOriginPixels(-5);
                 } else if (e.getKeyCode() == KeyEvent.VK_HOME) {
-                    LongRunningTask.submit(new Runnable() {
-
-                        public void run() {
-                            getViewContext().shiftOriginPixels(-getWidth());
-                        }
-                    });
+                    WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
+                    try {
+                        getViewContext().shiftOriginPixels(-getWidth());
+                        getViewContext().recordHistory();
+                    } finally {
+                        WaitCursorManager.removeWaitCursor(token);
+                    }
 
 
                 } else if (e.getKeyCode() == KeyEvent.VK_END) {
-                    LongRunningTask.submit(new Runnable() {
-
-                        public void run() {
-                            getViewContext().shiftOriginPixels(getWidth());
-                        }
-                    });
+                    WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
+                    try {
+                        getViewContext().shiftOriginPixels(getWidth());
+                        getViewContext().recordHistory();
+                    } finally {
+                        WaitCursorManager.removeWaitCursor(token);
+                    }
                 } else if (e.getKeyCode() == KeyEvent.VK_PLUS) {
                 } else if (e.getKeyCode() == KeyEvent.VK_MINUS) {
                 }
@@ -787,7 +706,7 @@ public class DataPanel extends TrackSetComponent {
             adjustedX = attributePanelEnd.intValue();
 
             if (width > 0) {
-                adjustedX += View.X_SPACING_BETWEEN_COMPONENTS;
+                adjustedX += IGVPanel.X_SPACING_BETWEEN_COMPONENTS;
             }
         }
 
@@ -805,7 +724,7 @@ public class DataPanel extends TrackSetComponent {
 
         Container parent = (Container) getParent();
         if (parent != null) {
-            int viewportWidth = ((TrackSetView) parent).getViewportWidth();
+            int viewportWidth = ((TrackPanel) parent).getViewportWidth();
 
             int parentVisibleWidth = ((JPanel) parent).getVisibleRect().width;
             if (parentVisibleWidth < viewportWidth) {
@@ -829,7 +748,7 @@ public class DataPanel extends TrackSetComponent {
      * @return
      */
     public Collection<TrackGroup> getTrackGroups() {
-        TrackSetView dataTrackView = (TrackSetView) getParent();
+        TrackPanel dataTrackView = (TrackPanel) getParent();
         return dataTrackView.getGroups();
     }
 
@@ -839,20 +758,10 @@ public class DataPanel extends TrackSetComponent {
      * @return
      */
     public int getVisibleHeight() {
-        TrackSetView dataTrackView = (TrackSetView) getParent();
+        TrackPanel dataTrackView = (TrackPanel) getParent();
         return dataTrackView.getVisibleRect().height;
     }
 
-    /**
-     * Method description
-     */
-    public void clearImageCache() {
-
-        if (cachedDataPanelPainter == null) {
-            return;
-        }
-        cachedDataPanelPainter.clearImageCache();
-    }
 
     /**
      * Some performance hings from the Mac developer mailing list.  Might improve
@@ -865,18 +774,12 @@ public class DataPanel extends TrackSetComponent {
     private void applyMacPerformanceHints(Graphics2D g2D) {
 
         // From mac mailint list.  Might help performance ?
-        g2D.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
-                RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
-        g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
-                RenderingHints.VALUE_ANTIALIAS_OFF);
-        g2D.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
-                RenderingHints.VALUE_COLOR_RENDER_SPEED);
-        g2D.setRenderingHint(RenderingHints.KEY_DITHERING,
-                RenderingHints.VALUE_DITHER_DISABLE);
-        g2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
-                RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
-        g2D.setRenderingHint(RenderingHints.KEY_RENDERING,
-                RenderingHints.VALUE_RENDER_SPEED);
+        g2D.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
+        g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
+        g2D.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
+        g2D.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
+        g2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
+        g2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
 
 
     }
@@ -885,12 +788,14 @@ public class DataPanel extends TrackSetComponent {
 
         // TODO -- a static member on the frame class is probably not
         // the best place for this.
+
         @Override
         public void mouseEntered(MouseEvent e) {
         }
 
         @Override
         public void mouseExited(MouseEvent e) {
+            IGVMainFrame.getInstance().setSelectedRegion(null);
         }
 
         /**
@@ -900,43 +805,29 @@ public class DataPanel extends TrackSetComponent {
         @Override
         public void mousePressed(final MouseEvent e) {
 
-            if (log.isDebugEnabled()) {
-                log.debug("enter mousePressed");
-            }
             if (SwingUtilities.getWindowAncestor(DataPanel.this).isActive()) {
                 DataPanel.this.requestFocus();
                 //DataPanel.this.grabFocus();
             }
 
             if (e.isPopupTrigger()) {
-                TrackManager.getInstance().clearSelections();
+                IGVMainFrame.getInstance().getTrackManager().clearSelections();
                 selectTracks(e);
                 openPopupMenu(e);
-
             }
         }
 
         @Override
         public void mouseMoved(MouseEvent e) {
-            final int x = e.getX();
-            final int y = e.getY();
+            tooltipTextPosition = e.getPoint();
 
-            if (tooltipTextPosition == null) {
-                tooltipTextPosition = new Position();
-            }
-            tooltipTextPosition.x = x;
-            tooltipTextPosition.y = y;
         }
 
         @Override
         public void mouseReleased(MouseEvent e) {
 
-            if (log.isDebugEnabled()) {
-                log.debug("Enter mouseReleased");
-            }
-
             if (e.isPopupTrigger()) {
-                TrackManager.getInstance().clearSelections();
+                IGVMainFrame.getInstance().getTrackManager().clearSelections();
                 selectTracks(e);
                 openPopupMenu(e);
             }
@@ -946,9 +837,9 @@ public class DataPanel extends TrackSetComponent {
 
     private class FileDropTargetListener implements DropTargetListener {
 
-        private DataPanel panel;
+        private TrackPanel panel;
 
-        public FileDropTargetListener(DataPanel dataPanel) {
+        public FileDropTargetListener(TrackPanel dataPanel) {
             panel = dataPanel;
         }
 
@@ -984,24 +875,30 @@ public class DataPanel extends TrackSetComponent {
 
             Transferable transferable = event.getTransferable();
 
+            DataFlavor[] flavors = transferable.getTransferDataFlavors();
+
             MessageCollection messages = new MessageCollection();
             try {
-                List<File> files = (List<File>) transferable.getTransferData(
-                        DataFlavor.javaFileListFlavor);
 
-                for (File file : files) {
-                    try {
-                        TrackManager.getInstance().load(file,
-                                panel.getTrackSetID());
-                    }
-                    catch (DataLoadException de) {
-                        messages.append(de.getMessage());
+                String obj = transferable.getTransferData(DataFlavor.stringFlavor).toString();
+                if (obj.startsWith("http:")) {
+                      IGVMainFrame.getInstance().loadTracks(Arrays.asList(new ResourceLocator(obj)));
+                } else {
+                    List<File> files = (List<File>) transferable.getTransferData(DataFlavor.javaFileListFlavor);
+
+                    for (File file : files) {
+                        try {
+
+                            IGVMainFrame.getInstance().getTrackManager().load(file, panel);
+                        }
+                        catch (DataLoadException de) {
+                            messages.append(de.getMessage());
+                        }
                     }
                 }
 
                 if (messages != null && !messages.isEmpty()) {
-                    UIUtilities.showAndLogErrorMessage(IGVMainFrame.getInstance(),
-                            messages.getFormattedMessage(), log);
+                    MessageUtils.showAndLogErrorMessage(IGVMainFrame.getInstance(), messages.getFormattedMessage(), log);
                 }
             }
             catch (Exception e) {
diff --git a/src/org/broad/igv/ui/panel/DataPanelImageManager.java b/src/org/broad/igv/ui/panel/DataPanelImageManager.java
deleted file mode 100644
index 63bbd0d..0000000
--- a/src/org/broad/igv/ui/panel/DataPanelImageManager.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.ui.panel;
-
-import org.apache.log4j.Logger;
-import org.broad.igv.track.RenderContext;
-import org.broad.igv.track.TrackGroup;
-import org.broad.igv.ui.IGVModel;
-import org.broad.igv.ui.ViewContext;
-import org.broad.igv.ui.util.GenericUtilities;
-import org.broad.igv.util.LRUCache;
-
-import javax.swing.*;
-import java.awt.*;
-import java.awt.image.BufferedImage;
-import java.util.Collection;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/**
- * @author jrobinso
- */
-public class DataPanelImageManager {
-
-    private static Logger log = Logger.getLogger(DataPanelImageManager.class);
-    public static int CACHE_SIZE = 8;
-    public static int IMAGE_WIDTH = 500;  // Width of each tile in pixels
-    private DataPanelPainter painter = new DataPanelPainter();
-    private LRUCache<String, CachedImageWrapper> cache = new LRUCache(CACHE_SIZE);
-    private ExecutorService threadExecutor;
-
-
-    // Creating a thread pool with a single instance.  Profiling has revealed lots of thread
-    // contention for synchronized objects during image creation.
-    public DataPanelImageManager() {
-        threadExecutor = Executors.newFixedThreadPool(1);
-
-    }
-
-    public void clearCache() {
-        cache.clear();
-    }
-
-    public int size() {
-        return cache.size();
-    }
-
-    public CachedImageWrapper getCachedImage(JComponent requestor, final String chr, int t, final int zoomLevel) {
-        String key = generateTileKey(chr, t, zoomLevel);
-        CachedImageWrapper wi = cache.get(key);
-        if (wi == null) {
-            log.error("Missing image for key: " + key);
-        }
-        return wi;
-
-    }
-
-    /**
-     * Build an image for the given groups, orign, and resolution scale
-     * and store it with the given key.
-     *
-     * @param key
-     * @param groups
-     * @param origin      in base pairs (not screen pixels)
-     * @param scale       in base pairs per pixel
-     * @param imageHeight in pixels
-     * @param background
-     */
-    public void buildImage(
-            Collection<TrackGroup> groups,
-            String key,
-            int yPosition,
-            double origin,
-            double scale,
-            int imageHeight,
-            Color background) {
-
-
-        if (cache.containsKey(key)) {
-            // already have this image.  Nothing to do
-        } else {
-            BufferedImage image = createImage(groups, origin, imageHeight, background);
-            CachedImageWrapper wrapper = new CachedImageWrapper(yPosition, origin, scale, image);
-            synchronized (cache) {
-                cache.put(key, wrapper);
-            }
-        }
-
-    }
-
-    private BufferedImage createImage(Collection<TrackGroup> groups, double origin, int imageHeight, Color background) {
-        // Create an image the size of the track panel
-        BufferedImage image = GenericUtilities.getDeviceCompatibleImage(IMAGE_WIDTH, imageHeight);
-        // paint image starting at the new offset and then store it
-        Graphics2D g = (Graphics2D) image.getGraphics();
-
-        ViewContext vc = IGVModel.getInstance().getViewContext();
-        double scale = vc.getScale();
-        String chromosomeName = vc.getChrName();
-        double startLocation = origin;
-        double endLocation = (startLocation + IMAGE_WIDTH * scale + 1);
-        int zoom = vc.getZoom();
-
-        RenderContext context =
-                new RenderContext(chromosomeName, startLocation, endLocation, zoom,
-                        origin, scale, null, g);
-
-        painter.paint(groups, context, IMAGE_WIDTH, imageHeight, background);
-        image.flush();
-        g.dispose();
-        long availableMemory = Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory();
-        if (availableMemory < 100000000) {
-            Runtime.getRuntime().gc();
-        }
-        return image;
-
-    }
-
-    public CachedImageWrapper getCachedImageWrapper(String key) {
-        return cache.get(key);
-    }
-
-    public void submit(Callable callable) {
-        threadExecutor.submit(callable);
-    }
-
-    java.lang.String generateTileKey(final String chr, int t, final int zoomLevel) {
-
-        // Fetch image for this chromosome, zoomlevel, and tile.  If found
-        // draw immediately
-        java.lang.String key = chr + "_z_" + zoomLevel + "_t_" + t;
-        return key;
-    }
-}
diff --git a/src/org/broad/igv/ui/panel/DataPanelPainter.java b/src/org/broad/igv/ui/panel/DataPanelPainter.java
index 426bc07..c2a7bef 100644
--- a/src/org/broad/igv/ui/panel/DataPanelPainter.java
+++ b/src/org/broad/igv/ui/panel/DataPanelPainter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -26,10 +26,11 @@ package org.broad.igv.ui.panel;
 //~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
-import org.broad.igv.track.*;
-import org.broad.igv.ui.IGVModel;
-import org.broad.igv.ui.ViewContext;
+import org.broad.igv.track.RenderContext;
+import org.broad.igv.track.Track;
+import org.broad.igv.track.TrackGroup;
+import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.UIConstants;
 
 import java.awt.*;
 import java.util.ArrayList;
@@ -44,34 +45,6 @@ public class DataPanelPainter {
 
     private static Logger log = Logger.getLogger(DataPanelPainter.class);
 
-    private ViewContext getViewContext() {
-        return IGVModel.getInstance().getViewContext();
-    }
-
-    /**
-     * Method description
-     *
-     * @param groups
-     * @param context
-     * @param width
-     * @param height
-     * @param background
-     */
-    public void paint(Collection<TrackGroup> groups, RenderContext context, int width, int height,
-                      Color background) {
-        paint(groups, context, width, height, background, null);
-    }
-
-    /**
-     * Method description
-     *
-     * @param groups
-     * @param context
-     * @param width
-     * @param height
-     * @param background
-     * @param visibleRect
-     */
     public void paint(Collection<TrackGroup> groups, RenderContext context, int width, int height,
                       Color background, Rectangle visibleRect) {
 
@@ -79,14 +52,13 @@ public class DataPanelPainter {
 
         try {
             graphics2D = (Graphics2D) context.getGraphics().create();
-            String chromosomeName = context.getChr();
 
             graphics2D.setBackground(background);
             graphics2D.clearRect(0, 0, width, height);
             graphics2D.setColor(Color.BLACK);
 
             final Graphics2D greyGraphics =
-                    context.getGraphic2DForColor(IGVConstants.VERY_LIGHT_GRAY);
+                    context.getGraphic2DForColor(UIConstants.VERY_LIGHT_GRAY);
             int trackX = 0;
             int trackY = 0;
 
@@ -100,8 +72,8 @@ public class DataPanelPainter {
 
                 if (group.isVisible()) {
                     if (groups.size() > 1) {
-                        greyGraphics.fillRect(0, trackY + 1, width, IGVConstants.groupGap - 1);
-                        trackY += IGVConstants.groupGap;
+                        greyGraphics.fillRect(0, trackY + 1, width, UIConstants.groupGap - 1);
+                        trackY += UIConstants.groupGap;
                     }
 
                     // Draw a line just above group.   TODO use Java2D line
@@ -128,12 +100,6 @@ public class DataPanelPainter {
 
                         if (track.isVisible()) {
 
-                            // This code fixes IGV-348
-                            if ((track instanceof FeatureTrack)
-                                    && ((FeatureTrack) track).isExpanded()) {
-                                //    ((FeatureTrack) track).getFeaturesByLevels(chromosomeName, 0, 0);
-                            }
-
                             trackY = draw(track, trackX, trackY, width, trackHeight, context);
                         }
                     }
@@ -154,7 +120,7 @@ public class DataPanelPainter {
                 TrackGroup group = groupIter.next();
                 if (group.isVisible()) {
                     if (groups.size() > 1) {
-                        trackY += IGVConstants.groupGap;
+                        trackY += UIConstants.groupGap;
                     }
 
                     // Draw borders  -- copy track list to prevent concurrent modification exception
@@ -178,8 +144,7 @@ public class DataPanelPainter {
         }
     }
 
-    final private int draw(Track track, int trackX, int trackY, int trackWidth, int trackHeight,
-                           RenderContext context) {
+    final private int draw(Track track, int trackX, int trackY, int trackWidth, int trackHeight, RenderContext context) {
 
 
         // The rectangle in which the track is drawn.  The height is reduced
@@ -190,7 +155,7 @@ public class DataPanelPainter {
 
         // Get overlays
 
-        List<Track> overlayTracks = TrackManager.getInstance().getOverlayTracks(track);
+        List<Track> overlayTracks = IGVMainFrame.getInstance().getTrackManager().getOverlayTracks(track);
         if (overlayTracks != null) {
             for (Track overlayTrack : overlayTracks) {
 
diff --git a/src/org/broad/igv/ui/panel/DragEvent.java b/src/org/broad/igv/ui/panel/DragEvent.java
index cbadd2e..ce15f57 100644
--- a/src/org/broad/igv/ui/panel/DragEvent.java
+++ b/src/org/broad/igv/ui/panel/DragEvent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/panel/DragEventManager.java b/src/org/broad/igv/ui/panel/DragEventManager.java
index 955d27f..cce4227 100644
--- a/src/org/broad/igv/ui/panel/DragEventManager.java
+++ b/src/org/broad/igv/ui/panel/DragEventManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/panel/DragListener.java b/src/org/broad/igv/ui/panel/DragListener.java
index 426b18e..68c98f8 100644
--- a/src/org/broad/igv/ui/panel/DragListener.java
+++ b/src/org/broad/igv/ui/panel/DragListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/panel/HeaderPanel.java b/src/org/broad/igv/ui/panel/HeaderPanel.java
index 05bf4ef..66e3c67 100644
--- a/src/org/broad/igv/ui/panel/HeaderPanel.java
+++ b/src/org/broad/igv/ui/panel/HeaderPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -104,7 +104,7 @@ public class HeaderPanel extends JPanel {
             adjustedX = attributePanelEnd.intValue();
 
             if (width > 0)
-                adjustedX += View.X_SPACING_BETWEEN_COMPONENTS;
+                adjustedX += IGVPanel.X_SPACING_BETWEEN_COMPONENTS;
         }
 
         return adjustedX;
diff --git a/src/org/broad/igv/ui/panel/IGVPanel.java b/src/org/broad/igv/ui/panel/IGVPanel.java
new file mode 100644
index 0000000..c073418
--- /dev/null
+++ b/src/org/broad/igv/ui/panel/IGVPanel.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.ui.panel;
+
+import org.broad.igv.ui.util.Packable;
+import org.broad.igv.ui.util.UIUtilities;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * A panel class specific to the IGV main window.  Lays out its components in a very specific way so that
+ * children (i.e. name, attribute, and data panels) from all instances align vertically
+ */
+public class IGVPanel extends JPanel {
+
+    public static int X_SPACING_BETWEEN_COMPONENTS = 10;
+
+    private String viewName = "IGVPanel";
+
+    public int getViewportHeight() {
+
+        Container parent = getParent();
+        return parent == null ? 0 : parent.getHeight();
+    }
+
+    /**
+     * Scrollpane height
+     *
+     * @return
+     */
+    public int getScrollPaneHeight() {
+
+        int height = 0;
+        Container parent = getParent();
+
+        if (parent instanceof JViewport) {
+            height = ((JViewport) parent).getParent().getHeight();
+        } else {
+            height = parent.getHeight();
+        }
+
+        return height;
+    }
+
+    public int getViewportWidth() {
+
+        int width = 0;
+        Container parent = getParent();
+
+        if (parent instanceof JViewport) {
+            width = (int) ((JViewport) parent).getViewSize().getWidth();
+        } else {
+            width = parent.getWidth();
+        }
+
+        return width;
+    }
+
+
+    public JViewport getViewport() {
+
+        JViewport viewport = null;
+        Container parent = getParent();
+
+        if (parent instanceof JViewport) {
+            viewport = ((JViewport) parent);
+        }
+        return viewport;
+    }
+
+    public JScrollPane getScrollPane() {
+
+        JScrollPane scollpane = null;
+        Container parent = getParent();
+
+        if (parent instanceof JViewport) {
+            scollpane = (JScrollPane) ((JViewport) parent).getParent();
+        }
+        return scollpane;
+    }
+
+    public void setHeight(int height) {
+
+        if (height < 0) {
+            height = 0;
+        }
+
+        // First resize itself
+        final Dimension dimension = new Dimension(getWidth(), height);
+        setSize(dimension);
+        setPreferredSize(dimension);
+
+        // Then resize its children
+        Component[] children = getComponents();
+        for (final Component child : children) {
+
+            final Dimension childDimension =
+                    new Dimension(child.getWidth(), height);
+
+            UIUtilities.invokeOnEventThread(new Runnable() {
+
+                public void run() {
+                    child.setSize(childDimension);
+                    child.setPreferredSize(childDimension);
+                }
+            });
+        }
+    }
+
+    public String getViewName() {
+        return viewName;
+    }
+
+    public void setViewName(String viewName) {
+        this.viewName = viewName;
+    }
+
+    public void packView() {
+
+        UIUtilities.invokeOnEventThread(new Runnable() {
+
+            public void run() {
+
+                Integer attributePanelEnd = null;
+                int width = 0;
+
+                // Pack the attribute panels
+                Component[] children = getComponents();
+                for (final Component child : children) {
+                    if (child instanceof Packable) {
+                        ((Packable) child).packComponent();
+
+                        if (attributePanelEnd == null) {
+                            int x = child.getX();
+                            width = child.getWidth();
+                            attributePanelEnd = new Integer(x + width);
+                        }
+                    }
+                }
+
+                // Change Track and Header panel X location
+                if (attributePanelEnd != null) {
+
+                    children = getComponents();
+                    for (final Component child : children) {
+
+                        if (child instanceof DataPanel ||
+                                child instanceof HeaderPanel) {
+
+                            if (width > 0) {
+                                attributePanelEnd += X_SPACING_BETWEEN_COMPONENTS;
+                            }
+                            child.setLocation((attributePanelEnd), child.getY());
+                        }
+                    }
+                }
+
+            }
+        });
+    }
+}
diff --git a/src/org/broad/igv/ui/panel/InfoPanel.java b/src/org/broad/igv/ui/panel/InfoPanel.java
deleted file mode 100644
index c6d6450..0000000
--- a/src/org/broad/igv/ui/panel/InfoPanel.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.ui.panel;
-
-import com.jidesoft.swing.JideBoxLayout;
-import com.jidesoft.swing.JideButton;
-import org.broad.igv.ui.FontManager;
-
-import javax.swing.*;
-import java.awt.*;
-
-/**
- * Information panel.  Displayed in upper left corner
- *
- * @author jrobinso
- */
-public class InfoPanel extends JPanel {
-
-    JideButton genomeButton;
-    JideButton helpButton;
-
-    public InfoPanel() {
-        initComponents();
-
-    }
-
-    @Override
-    public void setLayout(LayoutManager layoutManager) {
-        // Ignore layout manager settings
-    }
-
-    JEditorPane infoArea;
-
-    @Override
-    public void paintComponent(Graphics g) {
-        super.paintComponent(g);
-
-    }
-
-    protected void initComponents() {
-
-        JideBoxLayout layout = new JideBoxLayout(this, JideBoxLayout.Y_AXIS, 2);
-        super.setLayout(layout);
-
-
-        this.add(Box.createHorizontalStrut(10), JideBoxLayout.FIX);
-
-        /* helpButton = new JideButton("Getting started ...");
-        helpButton.setForeground(Color.BLUE.darker());
-        helpButton.setButtonStyle(ButtonStyle.HYPERLINK_STYLE);
-        helpButton.setHorizontalAlignment(SwingConstants.LEFT);
-        helpButton.addActionListener(new ActionListener() {
-        public void actionPerformed(ActionEvent actionevent) {
-        JOptionPane.showMessageDialog(null, "Under construction");
-        }
-        });
-        this.add(helpButton, JideBoxLayout.FIX);
-         * */
-
-
-        infoArea = new JEditorPane();
-        infoArea.setContentType("text/html");
-        infoArea.setFont(FontManager.getScalableFont(12));
-        // infoArea.setBorder(BorderFactory.createEmptyBorder());
-
-        infoArea.setMargin(new Insets(5, 5, 5, 5));
-        infoArea.setEditable(false);
-        //infoArea.setLineWrap(true);
-        //infoArea.setWrapStyleWord(true);
-        //addQuickstartLink();
-        this.add(infoArea, JideBoxLayout.VARY);
-
-        //updateHelpText();
-
-
-    }
-
-    public void updateHelpText() {
-        /*infoArea.setText("<html>" + getHelpText()); 
-        +
-        "<p><a href=\"" + SERVER_BASE_URL + "doc/quickstart.html\">Quick start guide</a>");
-        
-        infoArea.addHyperlinkListener(new HyperlinkListener() {
-
-        public void hyperlinkUpdate(HyperlinkEvent hyperlinkevent) {
-        try {
-        if (hyperlinkevent.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
-        BrowserLauncher.openURL(hyperlinkevent.getURL().toString());
-        }
-        } catch (IOException ex) {
-        com.jidesoft.dialog.JideOptionPane.showMessageDialog(infoArea, "Cannot connect to tutorial.");
-        }
-        }
-        });
-         * */
-    }
-
-    private String getHelpText() {
-        return "";
-    }
-}
diff --git a/src/org/broad/igv/ui/panel/MouseableRegion.java b/src/org/broad/igv/ui/panel/MouseableRegion.java
index 32e8020..c1ae870 100644
--- a/src/org/broad/igv/ui/panel/MouseableRegion.java
+++ b/src/org/broad/igv/ui/panel/MouseableRegion.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,7 +23,7 @@
 package org.broad.igv.ui.panel;
 
 import org.broad.igv.track.Track;
-import org.broad.igv.track.TrackManager;
+import org.broad.igv.ui.IGVMainFrame;
 
 import java.awt.*;
 import java.util.HashSet;
@@ -38,17 +38,15 @@ public class MouseableRegion {
     private String text;
     private TrackCltn trackCltn;
 
-    /**
-     *
-     */
+
     public MouseableRegion(Shape region, Track track) {
 
         this.region = region;
 
-        if (track.getId().equals(track.getDisplayName())) {
-            this.text = track.getDisplayName();
+        if (track.getName().equals(track.getName())) {
+            this.text = track.getName();
         } else {
-            this.text = "<html>" + track.getDisplayName() + "<br>" + track.getId();
+            this.text = "<html>" + track.getName() + "<br>" + track.getName();
         }
         trackCltn = new SingleTrackRegion(track);
     }
@@ -60,58 +58,25 @@ public class MouseableRegion {
         trackCltn = new AttributePanelRegion(key, value);
     }
 
-    public boolean containsTrack(Track t) {
-        return trackCltn.contains(t);
-    }
-
-    /**
-     * Method description
-     *
-     * @param x
-     * @param y
-     * @return
-     */
     public boolean containsPoint(double x, double y) {
         return region.contains(x, y);
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public Rectangle getBounds() {
         return region.getBounds();
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     public String getText() {
         return text;
     }
 
-    /**
-     *
-     */
-    public boolean hasTracks() {
-        return trackCltn.hasTracks();
-    }
-
-    /**
-     *
-     */
     public Set<Track> getTracks() {
         return trackCltn.getTracks();
     }
 
-    /**
-     * Method description
-     *
-     * @return
-     */
+
     @Override
     public String toString() {
         return region.toString();
@@ -119,8 +84,6 @@ public class MouseableRegion {
 
     interface TrackCltn {
 
-        public boolean hasTracks();
-
         public Set<Track> getTracks();
 
         public boolean contains(Track t);
@@ -130,21 +93,11 @@ public class MouseableRegion {
 
         private Set<Track> tracks;
 
-        /**
-         * Constructs ...
-         *
-         * @param region
-         * @param track
-         */
         public SingleTrackRegion(Track track) {
             tracks = new HashSet<Track>();
             tracks.add(track);
         }
 
-        public boolean hasTracks() {
-            return ((tracks == null) || tracks.isEmpty()) ? false : true;
-        }
-
         public Set<Track> getTracks() {
             return tracks;
         }
@@ -159,25 +112,15 @@ public class MouseableRegion {
         private String key;
         private String value;
 
-        /**
-         * Constructs ...
-         *
-         * @param region
-         * @param key
-         * @param value
-         */
+
         public AttributePanelRegion(String key, String value) {
             this.key = key;
             this.value = value;
         }
 
-        public boolean hasTracks() {
-            return (getTracks().isEmpty()) ? false : true;
-        }
-
         public Set<Track> getTracks() {
             Set<Track> selectedTracks = new HashSet();
-            for (Track track : TrackManager.getInstance().getAllTracks(true)) {
+            for (Track track : IGVMainFrame.getInstance().getTrackManager().getAllTracks(true)) {
                 String attributeValue = track.getAttributeValue(key);
                 if (attributeValue == null) {
                     continue;
diff --git a/src/org/broad/igv/ui/panel/NameHeaderPanel.java b/src/org/broad/igv/ui/panel/NameHeaderPanel.java
new file mode 100644
index 0000000..7dfe4d3
--- /dev/null
+++ b/src/org/broad/igv/ui/panel/NameHeaderPanel.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.ui.panel;
+
+import com.jidesoft.swing.JideBoxLayout;
+import com.jidesoft.swing.JideButton;
+import org.broad.igv.ui.FontManager;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * Information panel.  Displayed in upper left corner
+ *
+ * @author jrobinso
+ */
+public class NameHeaderPanel extends JPanel {
+
+    JideButton genomeButton;
+    JideButton helpButton;
+
+    public NameHeaderPanel() {
+        initComponents();
+
+    }
+
+    @Override
+    public void setLayout(LayoutManager layoutManager) {
+        // Ignore layout manager settings
+    }
+
+    JEditorPane infoArea;
+
+    @Override
+    public void paintComponent(Graphics g) {
+        super.paintComponent(g);
+
+    }
+
+    protected void initComponents() {
+
+        JideBoxLayout layout = new JideBoxLayout(this, JideBoxLayout.Y_AXIS, 2);
+        super.setLayout(layout);
+
+
+        this.add(Box.createHorizontalStrut(10), JideBoxLayout.FIX);
+
+        /* helpButton = new JideButton("Getting started ...");
+        helpButton.setForeground(Color.BLUE.darker());
+        helpButton.setButtonStyle(ButtonStyle.HYPERLINK_STYLE);
+        helpButton.setHorizontalAlignment(SwingConstants.LEFT);
+        helpButton.addActionListener(new ActionListener() {
+        public void actionPerformed(ActionEvent actionevent) {
+        JOptionPane.showMessageDialog(null, "Under construction");
+        }
+        });
+        this.add(helpButton, JideBoxLayout.FIX);
+         * */
+
+
+        infoArea = new JEditorPane();
+        infoArea.setContentType("text/html");
+        infoArea.setFont(FontManager.getScalableFont(12));
+        // infoArea.setBorder(BorderFactory.createEmptyBorder());
+
+        infoArea.setMargin(new Insets(5, 5, 5, 5));
+        infoArea.setEditable(false);
+        //infoArea.setLineWrap(true);
+        //infoArea.setWrapStyleWord(true);
+        //addQuickstartLink();
+        this.add(infoArea, JideBoxLayout.VARY);
+
+        //updateHelpText();
+
+
+    }
+
+    public void updateHelpText() {
+        /*infoArea.setText("<html>" + getHelpText()); 
+        +
+        "<p><a href=\"" + SERVER_BASE_URL + "doc/quickstart.html\">Quick start guide</a>");
+        
+        infoArea.addHyperlinkListener(new HyperlinkListener() {
+
+        public void hyperlinkUpdate(HyperlinkEvent hyperlinkevent) {
+        try {
+        if (hyperlinkevent.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
+        BrowserLauncher.openURL(hyperlinkevent.getURL().toString());
+        }
+        } catch (IOException ex) {
+        com.jidesoft.dialog.JideOptionPane.showMessageDialog(infoArea, "Cannot connect to tutorial.");
+        }
+        }
+        });
+         * */
+    }
+
+    private String getHelpText() {
+        return "";
+    }
+}
diff --git a/src/org/broad/igv/ui/panel/PanAndZoomTool.java b/src/org/broad/igv/ui/panel/PanAndZoomTool.java
index 81628db..5edc945 100644
--- a/src/org/broad/igv/ui/panel/PanAndZoomTool.java
+++ b/src/org/broad/igv/ui/panel/PanAndZoomTool.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,11 +23,13 @@ package org.broad.igv.ui.panel;
 
 //~--- non-JDK imports --------------------------------------------------------
 
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.track.Track;
 import org.broad.igv.ui.IGVMainFrame;
 import org.broad.igv.ui.IGVTool;
-import org.broad.igv.ui.LongRunningTask;
-import org.broad.igv.ui.ViewContext;
+import org.broad.igv.ui.WaitCursorManager;
+import org.broad.igv.util.LongRunningTask;
+import org.broad.igv.util.NamedRunnable;
 
 import javax.swing.*;
 import java.awt.*;
@@ -78,8 +80,7 @@ public class PanAndZoomTool extends IGVTool {
     @Override
     public void mousePressed(final MouseEvent e) {
 
-        isDragging = true;
-
+   
         panel = (Container) e.getSource();
 
         panel.setCursor(dragCursor);
@@ -115,6 +116,7 @@ public class PanAndZoomTool extends IGVTool {
             getViewContext().snapToGrid();
             isDragging = false;
             DragEventManager.getInstance().dragStopped();
+            getViewContext().recordHistory();
         }
         ((JComponent) e.getSource()).setCursor(getCursor());
     }
@@ -135,58 +137,64 @@ public class PanAndZoomTool extends IGVTool {
                 return;
             }
 
-            double deltaX = lastMousePoint.getX() - e.getX();
-            double deltaY = lastMousePoint.getY() - e.getY();
-            cumulativeDeltaX += Math.abs(deltaX);
-            cumulativeDeltaY += Math.abs(deltaY);
+            if (!isDragging && e.getPoint().distance(lastMousePoint) < 2) {
+                return;
+            } else {
+                isDragging = true;
 
-            // Test for horizontal vs vertical panning.
-            if (cumulativeDeltaX > cumulativeDeltaY) {
+                double deltaX = lastMousePoint.getX() - e.getX();
+                double deltaY = lastMousePoint.getY() - e.getY();
+                cumulativeDeltaX += Math.abs(deltaX);
+                cumulativeDeltaY += Math.abs(deltaY);
 
-                // Horizontal scrolling
-                getViewContext().shiftOriginPixels(deltaX);
-                return;
-            }
+                // Test for horizontal vs vertical panning.
+                if (cumulativeDeltaX > cumulativeDeltaY) {
+
+                    // Horizontal scrolling
+                    getViewContext().shiftOriginPixels(deltaX);
+                    return;
+                }
 
-            int totalYChange = lastMousePressedY - e.getY();
+                int totalYChange = lastMousePressedY - e.getY();
 
-            // Vertical Scrolling
-            if ((viewport != null) && (totalYChange != 0)) {
+                // Vertical Scrolling
+                if ((viewport != null) && (totalYChange != 0)) {
 
-                // This section handles false drag direction changes
-                int currentYDirection = 0;
-                try {
+                    // This section handles false drag direction changes
+                    int currentYDirection = 0;
+                    try {
 
-                    // Figure out the current drag direction
-                    currentYDirection = totalYChange / Math.abs(totalYChange);
+                        // Figure out the current drag direction
+                        currentYDirection = totalYChange / Math.abs(totalYChange);
 
-                    // If the previous direction is 0 we were not moving before
-                    if (previousYDirection != 0) {
+                        // If the previous direction is 0 we were not moving before
+                        if (previousYDirection != 0) {
 
-                        // See if we changed direction
-                        boolean changedYDirection = currentYDirection != previousYDirection;
-                        if (changedYDirection) {
+                            // See if we changed direction
+                            boolean changedYDirection = currentYDirection != previousYDirection;
+                            if (changedYDirection) {
 
-                            // Don't save lastMousePressedPoint because may
-                            // be incorrect (this is the problem we are
-                            // solving with the direction flag) instead
-                            // we'll just check the next drag Point to be
-                            // sure of the correct direction.
-                            return;
+                                // Don't save lastMousePressedPoint because may
+                                // be incorrect (this is the problem we are
+                                // solving with the direction flag) instead
+                                // we'll just check the next drag Point to be
+                                // sure of the correct direction.
+                                return;
+                            }
                         }
-                    }
 
-                } finally {
+                    } finally {
 
-                    // Save the current direction indicator for next time
-                    previousYDirection = currentYDirection;
-                }
+                        // Save the current direction indicator for next time
+                        previousYDirection = currentYDirection;
+                    }
 
-                // If we have a vertical scrollbar use it to move
-                if (verticalScrollBar != null) {
-                    int adjustedScrollbarValue = verticalScrollBar.getValue();
-                    adjustedScrollbarValue += totalYChange;
-                    verticalScrollBar.setValue(adjustedScrollbarValue);
+                    // If we have a vertical scrollbar use it to move
+                    if (verticalScrollBar != null) {
+                        int adjustedScrollbarValue = verticalScrollBar.getValue();
+                        adjustedScrollbarValue += totalYChange;
+                        verticalScrollBar.setValue(adjustedScrollbarValue);
+                    }
                 }
             }
         } finally {
@@ -222,12 +230,12 @@ public class PanAndZoomTool extends IGVTool {
                     : (e.isShiftDown() ? currentZoom + 3 : currentZoom + 1);
             final double locationClicked = viewContext.getChromosomePosition(e.getX());
 
-            LongRunningTask.submit(new Runnable() {
-
-                public void run() {
-                    viewContext.zoomTo(newZoom, locationClicked);
-                }
-            });
+            WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
+            try {
+                viewContext.zoomTo(newZoom, locationClicked);
+            } finally {
+                WaitCursorManager.removeWaitCursor(token);
+            }
         }
     }
 }
diff --git a/src/org/broad/igv/ui/panel/RegionOfInterestPanel.java b/src/org/broad/igv/ui/panel/RegionOfInterestPanel.java
index ba06e0f..bdeaac0 100644
--- a/src/org/broad/igv/ui/panel/RegionOfInterestPanel.java
+++ b/src/org/broad/igv/ui/panel/RegionOfInterestPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,13 +22,24 @@
  */
 package org.broad.igv.ui.panel;
 
-import org.broad.igv.ui.IGVModel;
+import org.broad.igv.feature.SequenceManager;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.track.RegionScoreType;
+import org.broad.igv.track.TrackType;
+import org.broad.igv.ui.IGVMainFrame;
 import org.broad.igv.ui.RegionOfInterest;
-import org.broad.igv.ui.ViewContext;
+import org.broad.igv.ui.util.MessageUtils;
+import org.broad.igv.util.LongRunningTask;
+import org.broad.igv.util.NamedRunnable;
 
 import javax.swing.*;
+import javax.swing.event.MouseInputAdapter;
 import java.awt.*;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
+import java.awt.event.*;
 import java.util.Collection;
+import java.util.Set;
 
 /**
  * @author eflakes
@@ -37,9 +48,15 @@ public class RegionOfInterestPanel extends JPanel {
 
     PopupMenu popup;
 
+    private RegionOfInterest selectedRegion = null;
+
     public RegionOfInterestPanel() {
 
         setToolTipText("Regions of Interest");
+
+        MouseInputAdapter ma = new ROIMouseAdapater();
+        addMouseListener(ma);
+        addMouseMotionListener(ma);
     }
 
     @Override
@@ -55,12 +72,12 @@ public class RegionOfInterestPanel extends JPanel {
     }
 
     Collection<RegionOfInterest> getRegions() {
-        return IGVModel.getInstance().getRegionsOfInterest(IGVModel.getInstance().getViewContext().getChrName());
+        return IGVMainFrame.getInstance().getSession().getRegionsOfInterest(ViewContext.getInstance().getChrName());
     }
 
     public void drawRegionsOfInterest(final Graphics2D g, int height) {
 
-        ViewContext viewContext = IGVModel.getInstance().getViewContext();
+        ViewContext viewContext = ViewContext.getInstance();
         Collection<RegionOfInterest> regions = getRegions();
 
         if (regions == null || regions.isEmpty()) {
@@ -90,4 +107,242 @@ public class RegionOfInterestPanel extends JPanel {
 
         }
     }
+
+
+    RegionOfInterest getRegionOfInterest(int px) {
+
+        double pos = ViewContext.getInstance().getChromosomePosition(px);
+
+        Collection<RegionOfInterest> roiList = getRegions();
+        if (roiList != null) {
+            for (RegionOfInterest roi : roiList) {
+                if (pos > roi.getStart() && pos < roi.getEnd()) {
+                    return roi;
+                }
+            }
+        }
+
+        return null;
+
+    }
+
+    protected JPopupMenu getPopupMenu(final RegionOfInterest roi) {
+
+        Set<TrackType> loadedTypes = IGVMainFrame.getInstance().getTrackManager().getLoadedTypes();
+
+        JPopupMenu popupMenu = new JPopupMenu();
+
+        if (loadedTypes.contains(TrackType.COPY_NUMBER) ||
+                loadedTypes.contains(TrackType.ALLELE_SPECIFIC_COPY_NUMBER) ||
+                loadedTypes.contains(TrackType.CNV)) {
+            JMenuItem item = new JMenuItem("Sort by amplification");
+            item.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+
+                    IGVMainFrame.getInstance().getTrackManager().sortByRegionScore(roi, RegionScoreType.AMPLIFICATION);
+                    IGVMainFrame.getInstance().getContentPane().repaint();
+                }
+            });
+            popupMenu.add(item);
+
+
+            item = new JMenuItem("Sort by deletion");
+            item.addActionListener(new ActionListener() {
+
+                public void actionPerformed(ActionEvent e) {
+
+                    IGVMainFrame.getInstance().getTrackManager().sortByRegionScore(roi, RegionScoreType.DELETION);
+                    IGVMainFrame.getInstance().getContentPane().repaint();
+                }
+            });
+            popupMenu.add(item);
+
+            item = new JMenuItem("Sort by breakpoint amplitudes");
+            item.addActionListener(new ActionListener() {
+
+                public void actionPerformed(ActionEvent e) {
+
+                    IGVMainFrame.getInstance().getTrackManager().sortByRegionScore(roi, RegionScoreType.FLUX);
+                    IGVMainFrame.getInstance().getContentPane().repaint();
+                }
+            });
+            popupMenu.add(item);
+        }
+
+        if (loadedTypes.contains(TrackType.GENE_EXPRESSION)) {
+            JMenuItem item = new JMenuItem("Sort by expression");
+            item.addActionListener(new ActionListener() {
+
+                public void actionPerformed(ActionEvent e) {
+
+                    IGVMainFrame.getInstance().getTrackManager().sortByRegionScore(roi, RegionScoreType.EXPRESSION);
+                    IGVMainFrame.getInstance().getContentPane().repaint();
+
+                }
+            });
+
+            popupMenu.add(item);
+        }
+
+        JMenuItem item = new JMenuItem("Sort by value");
+        item.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+
+                IGVMainFrame.getInstance().getTrackManager().sortByRegionScore(roi, RegionScoreType.SCORE);
+                IGVMainFrame.getInstance().getContentPane().repaint();
+
+            }
+        });
+        popupMenu.add(item);
+
+        popupMenu.addSeparator();
+
+        item = new JMenuItem("Zoom");
+        item.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+
+                ViewContext.getInstance().jumpTo(roi.getChr(), roi.getStart(), roi.getEnd());
+
+                String locusString = roi.getLocusString();
+                ViewContext.getInstance().history.push(locusString);
+
+            }
+        });
+        popupMenu.add(item);
+
+        item = new JMenuItem("Edit description...");
+        item.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+
+
+                String desc = JOptionPane.showInputDialog(RegionOfInterestPanel.this, "Add or edit region description:", roi.getDescription());
+                roi.setDescription(desc);
+
+            }
+        });
+        popupMenu.add(item);
+
+        item = new JMenuItem("Copy sequence");
+        item.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+
+                LongRunningTask.submit(new NamedRunnable() {
+                    public String getName() {
+                        return "Copy sequence";
+                    }
+
+                    public void run() {
+                        String genomeId = ViewContext.getInstance().getGenomeId();
+                        byte[] seqBytes = SequenceManager.readSequence(genomeId, roi.getChr(), roi.getStart(), roi.getEnd());
+                        if (seqBytes == null) {
+                            MessageUtils.showMessage("Sequence not available");
+                        } else {
+                            String sequence = new String(seqBytes);
+                            StringSelection stringSelection = new StringSelection(sequence);
+                            Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+                            clipboard.setContents(stringSelection, null);
+                        }
+                    }
+                });
+
+
+            }
+        });
+
+        // Disable copySequence if region exceeds a MB
+        if (roi.getEnd() - roi.getStart() > 1000000) {
+            item.setEnabled(false);
+        }
+        popupMenu.add(item);
+
+
+        popupMenu.add(new JSeparator());
+
+        item = new JMenuItem("Delete");
+        item.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                getRegions().remove(roi);
+                IGVMainFrame.getInstance().repaintDataAndHeaderPanels();
+            }
+        });
+        popupMenu.add(item);
+
+        return popupMenu;
+    }
+
+
+    public RegionOfInterest getSelectedRegion() {
+        return selectedRegion;
+    }
+
+    public void setSelectedRegion(RegionOfInterest selectedRegion) {
+        this.selectedRegion = selectedRegion;
+    }
+
+    class ROIMouseAdapater extends MouseInputAdapter {
+        /**
+         * Method description
+         *
+         * @param e
+         */
+        @Override
+        public void mousePressed(MouseEvent e) {
+            showPopup(e);
+        }
+
+        /**
+         * Method description
+         *
+         * @param e
+         */
+        @Override
+        public void mouseReleased(MouseEvent e) {
+            showPopup(e);
+        }
+
+
+        @Override
+        public void mouseMoved(MouseEvent e) {
+            RegionOfInterest roi = getRegionOfInterest(e.getX());
+            if (roi != null) {
+                setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+                setToolTipText(roi.getTooltip());
+                if (selectedRegion != roi) {
+                    selectedRegion = roi;
+                    IGVMainFrame.getInstance().repaintDataPanels();
+                }
+
+            } else {
+                if (selectedRegion != null) {
+                    selectedRegion = null;
+                    IGVMainFrame.getInstance().repaintDataPanels();
+                }
+                setToolTipText("");
+                setCursor(Cursor.getDefaultCursor());
+            }
+        }
+
+        @Override
+        public void mouseExited(MouseEvent mouseEvent) {
+            //if (selectedRegion != null) {
+            //    selectedRegion = null;
+            //    IGVMainFrame.getInstance().repaintDataPanels();
+            //}
+        }
+
+        private void showPopup(MouseEvent e) {
+
+            RegionOfInterest roi = getRegionOfInterest(e.getX());
+            if (roi != null) {
+
+                getPopupMenu(roi).show(e.getComponent(), e.getX(), e.getY());
+            }
+
+        }
+    }
 }
diff --git a/src/org/broad/igv/ui/panel/RulerPanel.java b/src/org/broad/igv/ui/panel/RulerPanel.java
index d900fe2..fc1db6d 100644
--- a/src/org/broad/igv/ui/panel/RulerPanel.java
+++ b/src/org/broad/igv/ui/panel/RulerPanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -26,12 +26,16 @@
  */
 package org.broad.igv.ui.panel;
 
-import static org.broad.igv.IGVConstants.CHR_ALL;
+import com.jidesoft.utils.SwingWorker;
+import org.apache.log4j.Logger;
+import org.broad.igv.Globals;
+import org.broad.igv.PreferenceManager;
 import org.broad.igv.feature.Chromosome;
 import org.broad.igv.feature.Genome;
-import org.broad.igv.ui.*;
-import org.broad.igv.PreferenceManager;
-import org.apache.log4j.Logger;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.ui.FontManager;
+import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.WaitCursorManager;
 
 import javax.swing.*;
 import javax.swing.event.MouseInputAdapter;
@@ -65,6 +69,16 @@ public class RulerPanel extends JPanel {
     private Font chrFont = FontManager.getScalableFont(10);
     private List<ClickLink> chromosomeRects = new ArrayList();
 
+    private static Color dragColor = new Color(.5f, .5f, 1f, .3f);
+    private static Color zoomBoundColor = new Color(0.5f, 0.5f, 0.5f);
+
+    boolean dragging = false;
+    int dragStart;
+    int dragEnd;
+    public static final String WHOLE_GENOME_TOOLTIP = "<html>Click on a chromosome number to jump to that chromosome," +
+            "<br>or click and drag to zoom in.";
+    public static final String CHROM_TOOLTIP = "Click and drag to zoom in.";
+
     public RulerPanel() {
         initialize();
     }
@@ -93,45 +107,39 @@ public class RulerPanel extends JPanel {
                 drawEllipsis(g);
             }
         }
-        /*
-       // Draw regions of interest?
-       ViewContext viewContext = IGVMainFrame.theInstance.getViewContext();
-       if(viewContext.isShowRegionsOfInterestOn()) {
-       boolean drawBackground = false;
-       viewContext.drawRegionsOfInterest(g, getHeight(), drawBackground);
-       }
-        */
-
-        /*
-       // Manually draw border because setBorder() shifts the layout
-       g.setColor(Color.BLACK);
-       int x = getX();
-       int y = getY();
-       int width = getWidth();
-       int height = getHeight();
-       g.drawLine(0, 0, 0, height); // left border
-       g.drawLine(width-1, 0, width-1, height); // right border
-        */
+
+
+        if (dragging) {
+            g.setColor(dragColor);
+            int start = Math.min(dragStart, dragEnd);
+            int w = Math.abs(dragEnd - dragStart);
+            final int height = getHeight();
+            g.fillRect(start, 0, w, height);
+
+            g.setColor(zoomBoundColor);
+            g.drawLine(dragStart, 0, dragStart, height);
+            g.drawLine(dragEnd, 0, dragEnd, height);
+        }
+
     }
 
     private ViewContext getViewContext() {
-        return IGVModel.getInstance().getViewContext();
+        return ViewContext.getInstance();
     }
 
     private void drawSpan(Graphics g) {
 
         //TODO -- hack
 
-        int w = getViewContext().getDataPanelWidth();
+        int w = getWidth();
 
         g.setFont(spanFont);
 
         // Positions are 1/2 open, subtract 1 to compensate
-        int range = (int) (getViewContext().getScale() * w) - 1;
+        int range = (int) (getViewContext().getScale() * w) + 1;
 
         TickSpacing ts = findSpacing(range);
-        String rangeString = formatNumber((double) range / ts.getUnitMultiplier()) +
-                " " + ts.getMajorUnit();
+        String rangeString = formatNumber((double) range / ts.getUnitMultiplier()) + " " + ts.getMajorUnit();
         int strWidth = g.getFontMetrics().stringWidth(rangeString);
         int strHeight = g.getFontMetrics().getAscent();
         int strPosition = (w - strWidth) / 2;
@@ -152,15 +160,13 @@ public class RulerPanel extends JPanel {
     }
 
     private void drawEllipsis(Graphics g) {
-        double cytobandScale =
-                ((double) getViewContext().getChromosomeLength()) / getViewContext().getDataPanelWidth();
+        double cytobandScale = ((double) getViewContext().getChromosomeLength()) / getWidth();
 
         double maxPixel = getViewContext().getMaxPixel();
         //visibleFraction = maxPixel < 0 ? 0 : ((double) getViewContext().getDataPanelWidth()) / maxPixel;
 
         int start = (int) ((getViewContext().getOrigin()) / cytobandScale);
-        int span =
-                (int) ((getViewContext().getDataPanelWidth() * getViewContext().getScale()) / cytobandScale);
+        int span = (int) ((getWidth() * getViewContext().getScale()) / cytobandScale);
         int end = start + span;
 
         g.drawLine(start, 0, 0, getHeight());
@@ -170,7 +176,7 @@ public class RulerPanel extends JPanel {
 
     private void drawTicks(Graphics g) {
 
-        int w = getViewContext().getDataPanelWidth();
+        int w = getWidth();
 
         g.setFont(tickFont);
 
@@ -213,7 +219,7 @@ public class RulerPanel extends JPanel {
         g.setFont(chrFont);
 
         Genome genome = getViewContext().getGenome();
-        if(genome == null) {
+        if (genome == null) {
             log.info("No genome found with id: " + getViewContext().getGenomeId());
             PreferenceManager.getInstance().remove(PreferenceManager.DEFAULT_GENOME_KEY);
         }
@@ -222,12 +228,12 @@ public class RulerPanel extends JPanel {
         long offset = 0;
         chromosomeRects.clear();
         List<String> chrNames = genome.getChromosomeNames();
-        if(chrNames == null) {
+        if (chrNames == null) {
             log.info("No chromosomes found for genome: " + genome.getId());
             PreferenceManager.getInstance().remove(PreferenceManager.DEFAULT_GENOME_KEY);
         }
-        if(chrNames.size() > 500) {
-           return;
+        if (chrNames.size() > 500) {
+            return;
         }
 
         for (String chrName : genome.getChromosomeNames()) {
@@ -273,7 +279,7 @@ public class RulerPanel extends JPanel {
             return new TickSpacing(1, "bp", 1);
         }
         // TODO -- hack, assumes location unit for whole genome is kilo-base
-        boolean scaleInKB = getViewContext().getChrName().equals(CHR_ALL);
+        boolean scaleInKB = getViewContext().getChrName().equals(Globals.CHR_ALL);
 
 
         // Now man zeroes?
@@ -302,13 +308,13 @@ public class RulerPanel extends JPanel {
 
     private void initialize() {
 
-        if (!isWholeGenomeView()) {
-            setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
-            this.setToolTipText("Click to center view.");
-        } else {
+        setBorder(BorderFactory.createLineBorder(new Color(0f, 0f, 0f, 0.2f)));
 
-            setCursor(Cursor.getDefaultCursor());
-            this.setToolTipText("Click on a chromosome number to jump to that chromosome.");
+        setCursor(Cursor.getDefaultCursor());
+        if (isWholeGenomeView()) {
+            this.setToolTipText(WHOLE_GENOME_TOOLTIP);
+        } else {
+            this.setToolTipText("Click and drag to zoom");
         }
 
         MouseInputAdapter mouseAdapter = new MouseInputAdapter() {
@@ -318,27 +324,29 @@ public class RulerPanel extends JPanel {
             @Override
             public void mouseClicked(MouseEvent evt) {
                 final MouseEvent e = evt;
-                LongRunningTask.submit(new Runnable() {
-
-                    public void run() {
-                        if (!isWholeGenomeView()) {
-                            double newLocation = IGVModel.getInstance().getViewContext().getChromosomePosition(e.getX());
-                            getViewContext().centerOnLocation(newLocation);
-                        } else {
-                            for (ClickLink link : chromosomeRects) {
-                                if (link.region.contains(e.getPoint())) {
-                                    getViewContext().setChrName(link.value);
-                                    // TODO -- get rid of this ugly reference to IGVMainFrame.theInstance
-                                    IGVMainFrame.getInstance().chromosomeChangeEvent();
-
-                                    return;
-                                }
-                                setCursor(Cursor.getDefaultCursor());
-
+                WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
+                try {
+
+                    if (!isWholeGenomeView()) {
+                        double newLocation = ViewContext.getInstance().getChromosomePosition(e.getX());
+                        getViewContext().centerOnLocation(newLocation);
+                    } else {
+                        for (ClickLink link : chromosomeRects) {
+                            if (link.region.contains(e.getPoint())) {
+                                getViewContext().setChrName(link.value);
+                                getViewContext().recordHistory();
+                                // TODO -- get rid of this ugly reference to IGVMainFrame.theInstance
+                                IGVMainFrame.getInstance().chromosomeChangeEvent();
+
+                                return;
                             }
+                            setCursor(Cursor.getDefaultCursor());
+
                         }
                     }
-                });
+                } finally {
+                    WaitCursorManager.removeWaitCursor(token);
+                }
             }
 
             @Override
@@ -352,23 +360,50 @@ public class RulerPanel extends JPanel {
                         }
                     }
                     setCursor(Cursor.getDefaultCursor());
-                    setToolTipText("Click on a chromosome number to jump to that chromosome.");
+                    setToolTipText(WHOLE_GENOME_TOOLTIP);
 
                 }
             }
 
             @Override
             public void mouseEntered(MouseEvent e) {
-
+                setCursor(Cursor.getDefaultCursor());
                 if (isWholeGenomeView()) {
-                    setCursor(Cursor.getDefaultCursor());
-                    setToolTipText("Click on a chromosome number to jump to that chromosome.");
+                    setToolTipText(WHOLE_GENOME_TOOLTIP);
                 } else {
-                    setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
-                    setToolTipText("Click on a location to center the view.");
+                    setToolTipText(CHROM_TOOLTIP);
                 }
 
             }
+
+
+            @Override
+            public void mouseDragged(MouseEvent e) {
+                if (Math.abs(e.getPoint().getX() - dragStart) > 1) {
+                    dragEnd = e.getX();
+                    dragging = true;
+                    repaint();
+                }
+            }
+
+            @Override
+            public void mousePressed(MouseEvent e) {
+                dragStart = e.getX();
+            }
+
+            @Override
+            public void mouseReleased(MouseEvent e) {
+                if (dragging) {
+                    dragEnd = e.getX();
+                    dragging = false;
+                    zoom();
+                }
+            }
+
+            //@Override
+            //public void mouseExited(MouseEvent e) {
+            //dragging = false;
+            //}
         };
 
         addMouseMotionListener(mouseAdapter);
@@ -376,6 +411,60 @@ public class RulerPanel extends JPanel {
         addMouseListener(mouseAdapter);
     }
 
+    private void zoom() {
+
+        SwingWorker worker = new SwingWorker() {
+
+            WaitCursorManager.CursorToken token;
+
+            @Override
+            protected Object doInBackground() throws Exception {
+
+                token = WaitCursorManager.showWaitCursor();
+                final ViewContext vc = getViewContext();
+                double s = vc.getChromosomePosition(dragStart);
+                double e = vc.getChromosomePosition(dragEnd);
+                if (e < s) {
+                    double tmp = s;
+                    s = e;
+                    e = tmp;
+                }
+                if (e - s < 40) {
+                    double c = (s + e) / 2;
+                    s = c - 20;
+                    e = c + 20;
+                }
+
+                String chr = null;
+                if (isWholeGenomeView()) {
+                    Genome genome = vc.getGenome();
+                    Genome.ChromosomeCoordinate start = genome.getChromosomeCoordinate((int) s);
+                    Genome.ChromosomeCoordinate end = genome.getChromosomeCoordinate((int) e);
+
+                    chr = start.getChr();
+                    s = start.getCoordinate();
+                    e = end.getCoordinate();
+                    if (end.getChr() != start.getChr()) {
+                        e = genome.getChromosome(start.getChr()).getLength();
+                    }
+                } else {
+                    chr = vc.getChrName();
+                }
+
+                vc.jumpTo(chr, (int) Math.min(s, e), (int) Math.max(s, e));
+                vc.recordHistory();
+                return null;
+            }
+
+            @Override
+            protected void done() {
+                WaitCursorManager.removeWaitCursor(token);
+            }
+        };
+
+        worker.execute();
+    }
+
     class TickSpacing {
 
         private double majorTick;
@@ -416,6 +505,7 @@ public class RulerPanel extends JPanel {
     }
 
     // TODO -- possibly generalize?
+
     class ClickLink {
 
         Rectangle region;
diff --git a/src/org/broad/igv/ui/panel/TrackNamePanel.java b/src/org/broad/igv/ui/panel/TrackNamePanel.java
index 2e0c2ea..e28764d 100644
--- a/src/org/broad/igv/ui/panel/TrackNamePanel.java
+++ b/src/org/broad/igv/ui/panel/TrackNamePanel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -25,26 +25,24 @@
  */
 package org.broad.igv.ui.panel;
 
-//~--- non-JDK imports --------------------------------------------------------
 
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
 import org.broad.igv.track.Track;
 import org.broad.igv.track.TrackGroup;
-import org.broad.igv.track.TrackManager;
-import org.broad.igv.ui.GuiUtilities;
+import org.broad.igv.track.TrackMenuUtils;
 import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.UIConstants;
 import org.broad.igv.ui.dnd.AbstractGhostDropManager;
 import org.broad.igv.ui.dnd.GhostDropEvent;
 import org.broad.igv.ui.dnd.GhostDropListener;
 import org.broad.igv.ui.dnd.GhostGlassPane;
+import org.broad.igv.ui.util.UIUtilities;
+import org.jdesktop.layout.GroupLayout;
 
 import javax.swing.*;
 import javax.swing.event.MouseInputAdapter;
 import java.awt.*;
-import java.awt.event.AdjustmentEvent;
-import java.awt.event.AdjustmentListener;
-import java.awt.event.MouseEvent;
+import java.awt.event.*;
 import java.awt.image.BufferedImage;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -54,33 +52,69 @@ import java.util.List;
 /**
  * @author jrobinso
  */
-public class TrackNamePanel extends TrackSetComponent implements AdjustmentListener {
+public class TrackNamePanel extends TrackPanelComponent implements AdjustmentListener {
 
     private static Logger log = Logger.getLogger(TrackNamePanel.class);
 
 
-    // private List<? extends Track> tracks = TrackManager.getInstance().getDataTracks();
-    /**
-     * Constructs ...
-     */
+    // TODO -- this use of a static is really bad,  bugs and memory leaks waiting to happen.  Redesign this.
     static List<DropListener> dropListeners = new ArrayList();
 
-    public TrackNamePanel() {
+
+    static void addGhostDropListener(DropListener listener) {
+        if (listener != null) {
+            dropListeners.add(listener);
+        }
+    }
+
+    public static void removeDropListenerFor(TrackNamePanel panel) {
+        List<DropListener> removeThese = new ArrayList();
+        for (DropListener dl : dropListeners) {
+            if (dl.panel == panel) {
+                removeThese.add(dl);
+            }
+        }
+        dropListeners.removeAll(removeThese);
+    }
+
+
+    List<GroupExtent> groupExtents = new ArrayList();
+
+    BufferedImage dndImage = null;
+
+    TrackGroup selectedGroup = null;
+
+    boolean showGroupNames = true;
+
+    boolean showSampleNamesWhenGrouped = false;
+
+
+    public TrackNamePanel(TrackPanel trackPanel) {
+        super(trackPanel);
         init();
     }
 
+    Collection<TrackGroup> getGroups() {
+        TrackPanel dataTrackView = (TrackPanel) getParent();
+        return dataTrackView.getGroups();
+    }
+
+    private boolean isGrouped() {
+        Collection<TrackGroup> groups = getGroups();
+        return groups.size() > 1;
+    }
+
+
     @Override
-    protected void paintComponent(Graphics g) {
+    public void paintComponent(Graphics g) {
 
         super.paintComponent(g);
 
         removeMousableRegions();
 
-        // Get the current tracks
-        TrackSetView dataTrackView = (TrackSetView) getParent();
-
         // Get available tracks
-        Collection<TrackGroup> groups = dataTrackView.getGroups();
+        Collection<TrackGroup> groups = getGroups();
+        boolean isGrouped = groups.size() > 1;
 
         Rectangle visibleRect = getVisibleRect();
 
@@ -89,55 +123,37 @@ public class TrackNamePanel extends TrackSetComponent implements AdjustmentListe
             graphics2D.setColor(Color.BLACK);
 
             final Graphics2D greyGraphics = (Graphics2D) g.create();
-            greyGraphics.setColor(IGVConstants.VERY_LIGHT_GRAY);
+            greyGraphics.setColor(UIConstants.VERY_LIGHT_GRAY);
 
-            int regionX = 0;
             int regionY = 0;
 
+            groupExtents.clear();
             for (Iterator<TrackGroup> groupIter = groups.iterator(); groupIter.hasNext();) {
                 TrackGroup group = groupIter.next();
 
                 if (group.isVisible()) {
-                    if (groups.size() > 1) {
-                        if (regionY + IGVConstants.groupGap >= visibleRect.y &&
-                                regionY < visibleRect.getMaxY()) {
-                            greyGraphics.fillRect(0, regionY + 1, getWidth(),
-                                    IGVConstants.groupGap - 1);
+                    if (isGrouped) {
+                        if (regionY + UIConstants.groupGap >= visibleRect.y && regionY < visibleRect.getMaxY()) {
+                            greyGraphics.fillRect(0, regionY + 1, getWidth(), UIConstants.groupGap - 1);
                         }
-                        regionY += IGVConstants.groupGap;
+                        regionY += UIConstants.groupGap;
                     }
 
-                    if (group.isDrawBorder() && regionY + IGVConstants.groupGap >= visibleRect.y &&
+                    if (group.isDrawBorder() && regionY + UIConstants.groupGap >= visibleRect.y &&
                             regionY < visibleRect.getMaxY()) {
                         g.drawLine(0, regionY - 1, getWidth(), regionY - 1);
                     }
 
-                    int previousRegion = -1;
-                    List<Track> tmp = new ArrayList(group.getTracks());
-                    for (Track track : tmp) {
-
-                        track.setTop(regionY);
-
-                        int trackHeight = track.getHeight();
-
-                        if (track.isVisible()) {
-
-                            previousRegion = regionY;
-
-                            if (regionY + trackHeight >= visibleRect.y &&
-                                    regionY < visibleRect.getMaxY()) {
-                                int width = getWidth();
-                                int height = track.getHeight();
-
-                                Rectangle region = new Rectangle(regionX,
-                                        regionY, width, height);
-                                addMousableRegion(new MouseableRegion(region, track));
-                                draw(graphics2D, track, regionX, regionY, width,
-                                        height, visibleRect);
-
-                            }
-                            regionY += trackHeight;
+                    int y = regionY;
+                    regionY = printTrackNames(group, visibleRect, graphics2D, 0, regionY);
 
+                    if (isGrouped) {
+                        int h = group.getHeight();
+                        groupExtents.add(new GroupExtent(group, y, y + h));
+                        if (showGroupNames) {
+                            Rectangle rect = new Rectangle(visibleRect.x, y, visibleRect.width, h);
+                            Rectangle displayableRect = getDisplayableRect(rect, visibleRect);
+                            group.renderName(graphics2D, displayableRect, group == selectedGroup);
                         }
                     }
 
@@ -150,30 +166,58 @@ public class TrackNamePanel extends TrackSetComponent implements AdjustmentListe
         }
     }
 
-    private void draw(Graphics2D graphics, Track track, int trackX, int trackY,
-                      int trackWidth,
-                      int trackHeight,
-                      Rectangle visibleRect) {
+    private Rectangle getDisplayableRect(Rectangle trackRectangle, Rectangle visibleRect) {
+        Rectangle rect = null;
+        if (visibleRect != null) {
+            Rectangle intersectedRect = trackRectangle.intersection(visibleRect);
+            if (intersectedRect.height > 15) {
+                rect = intersectedRect;
+            } else {
+                rect = new Rectangle(trackRectangle);
+            }
+        }
+        return rect;
+
+    }
+
+    private int printTrackNames(TrackGroup group, Rectangle visibleRect, Graphics2D graphics2D, int regionX, int regionY) {
+
+        List<Track> tmp = new ArrayList(group.getTracks());
+        for (Track track : tmp) {
+            track.setTop(regionY);
+            int trackHeight = track.getHeight();
+            if (track.isVisible()) {
 
-        Rectangle rect = new Rectangle(trackX, trackY, trackWidth, trackHeight);
+                if (regionY + trackHeight >= visibleRect.y && regionY < visibleRect.getMaxY()) {
+                    int width = getWidth();
+                    int height = track.getHeight();
 
-        track.renderName(graphics, rect, visibleRect);
+                    Rectangle region = new Rectangle(regionX, regionY, width, height);
+                    addMousableRegion(new MouseableRegion(region, track));
 
+                    if (!isGrouped() || showSampleNamesWhenGrouped) {
+                        Rectangle rect = new Rectangle(regionX, regionY, width, height);
+                        track.renderName(graphics2D, rect, visibleRect);
+                    }
+
+                }
+                regionY += trackHeight;
+            }
+        }
+        return regionY;
     }
 
+
     private void init() {
 
         setBorder(javax.swing.BorderFactory.createLineBorder(Color.black));
         setBackground(new java.awt.Color(255, 255, 255));
-        org.jdesktop.layout.GroupLayout dataTrackNamePanelLayout = new org.jdesktop.layout.GroupLayout(
-                this);
+        GroupLayout dataTrackNamePanelLayout = new org.jdesktop.layout.GroupLayout(this);
         setLayout(dataTrackNamePanelLayout);
         dataTrackNamePanelLayout.setHorizontalGroup(
-                dataTrackNamePanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING).add(
-                        0, 148, Short.MAX_VALUE));
+                dataTrackNamePanelLayout.createParallelGroup(GroupLayout.LEADING).add(0, 148, Short.MAX_VALUE));
         dataTrackNamePanelLayout.setVerticalGroup(
-                dataTrackNamePanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING).add(
-                        0, 528, Short.MAX_VALUE));
+                dataTrackNamePanelLayout.createParallelGroup(GroupLayout.LEADING).add(0, 528, Short.MAX_VALUE));
 
         NamePanelMouseAdapter mouseAdapter = new NamePanelMouseAdapter();
         addMouseListener(mouseAdapter);
@@ -183,20 +227,55 @@ public class TrackNamePanel extends TrackSetComponent implements AdjustmentListe
         addGhostDropListener(dndListener);
     }
 
-    /**
-     * Method description
-     *
-     * @param x
-     * @param y
-     * @return
-     */
+
+    @Override
+    protected void openPopupMenu(MouseEvent e) {
+
+        // If there is a single selected track give it a chance to handle the click
+        Collection<Track> selectedTracs = getSelectedTracks();
+        String title = getPopupMenuTitle(0, 0);
+
+        JPopupMenu menu = TrackMenuUtils.getEmptyPopup(title);
+
+        if (isGrouped()) {
+            final JMenuItem item = new JCheckBoxMenuItem("Show group names");
+            item.setSelected(showGroupNames);
+            item.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+                    showGroupNames = item.isSelected();
+                    clearTrackSelections();
+                    repaint();
+                }
+            });
+            menu.add(item);
+
+            final JMenuItem item2 = new JCheckBoxMenuItem("Show sample names");
+            item2.setSelected(showSampleNamesWhenGrouped);
+            item2.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+                    showSampleNamesWhenGrouped = item2.isSelected();
+                    clearTrackSelections();
+                    repaint();
+                }
+            });
+            menu.add(item2);
+            menu.addSeparator();
+        }
+
+        TrackMenuUtils.addStandardItems(menu, selectedTracs, e);
+
+        menu.show(e.getComponent(), e.getX(), e.getY());
+
+    }
+
+
     public String getPopupMenuTitle(int x, int y) {
 
         Collection<Track> tracks = getSelectedTracks();
 
         String popupTitle;
         if (tracks.size() == 1) {
-            popupTitle = tracks.iterator().next().getDisplayName();
+            popupTitle = tracks.iterator().next().getName();
         } else {
             popupTitle = "Total Tracks Selected: " + tracks.size();
         }
@@ -204,16 +283,10 @@ public class TrackNamePanel extends TrackSetComponent implements AdjustmentListe
         return popupTitle;
     }
 
-    /**
-     * Method description
-     *
-     * @param x
-     * @param y
-     * @return
-     */
+
     public String getTooltipTextForLocation(int x, int y) {
 
-        List<MouseableRegion> mouseableRegions = TrackNamePanel.this.getMouseableRegions();
+        List<MouseableRegion> mouseableRegions = TrackNamePanel.this.getTrackRegions();
 
         for (MouseableRegion mouseableRegion : mouseableRegions) {
             if (mouseableRegion.containsPoint(x, y)) {
@@ -223,18 +296,6 @@ public class TrackNamePanel extends TrackSetComponent implements AdjustmentListe
         return "";
     }
 
-    static void addGhostDropListener(DropListener listener) {
-        if (listener != null) {
-            dropListeners.add(listener);
-        }
-    }
-
-    static void removeGhostDropListener(DropListener listener) {
-        if (listener != null) {
-            dropListeners.remove(listener);
-        }
-    }
-
     /**
      * Listener for scroll pane events
      *
@@ -249,8 +310,6 @@ public class TrackNamePanel extends TrackSetComponent implements AdjustmentListe
 
     }
 
-    BufferedImage dndImage = null;
-
     private synchronized void createDnDImage() {
         dndImage = new BufferedImage(getWidth(), 2, BufferedImage.TYPE_INT_ARGB);
         Graphics g = dndImage.getGraphics();
@@ -261,18 +320,28 @@ public class TrackNamePanel extends TrackSetComponent implements AdjustmentListe
     }
 
     protected void shiftSelectTracks(MouseEvent e) {
-        for (MouseableRegion mouseRegion : mouseableRegions) {
+        for (MouseableRegion mouseRegion : trackRegions) {
             if (mouseRegion.containsPoint(e.getX(), e.getY())) {
                 Collection<Track> clickedTracks = mouseRegion.getTracks();
                 if (clickedTracks != null && clickedTracks.size() > 0) {
                     Track t = clickedTracks.iterator().next();
-                    TrackManager.getInstance().shiftSelectTracks(t);
+                    IGVMainFrame.getInstance().getTrackManager().shiftSelectTracks(t);
                 }
                 return;
             }
         }
     }
 
+
+    private TrackGroup getGroup(int y) {
+        for (GroupExtent ge : groupExtents) {
+            if (ge.contains(y)) {
+                return ge.group;
+            }
+        }
+        return null;
+    }
+
     /**
      * Mouse adapter for the track name panel.  Supports multiple selection,
      * popup menu, and drag & drop within or between name panels.
@@ -280,6 +349,7 @@ public class TrackNamePanel extends TrackSetComponent implements AdjustmentListe
     class NamePanelMouseAdapter extends MouseInputAdapter {
 
         boolean isDragging = false;
+        List<Track> dragTracks = new ArrayList();
         Point dragStart = null;
 
         @Override
@@ -292,28 +362,49 @@ public class TrackNamePanel extends TrackSetComponent implements AdjustmentListe
                 log.debug("Enter mousePressed");
             }
 
+            dragStart = e.getPoint();
+
             requestFocus();
             grabFocus();
 
+            boolean isGrouped = isGrouped();
+
             if (e.isPopupTrigger()) {
-                if (!isTrackSelected(e)) {
+                if (isGrouped) {
+
+                } else if (!isTrackSelected(e)) {
                     clearTrackSelections();
                     selectTracks(e);
                 }
                 openPopupMenu(e);
             } // meta (mac) or control,  toggle selection]
             else if (e.getButton() == MouseEvent.BUTTON1) {
-                if (e.isMetaDown() || e.isControlDown()) {
-                    toggleTrackSelections(e);
-                } else if (e.isShiftDown()) {
-                    shiftSelectTracks(e);
+
+                if (isGrouped) {
+                    clearTrackSelections();
+                    TrackGroup g = getGroup(e.getY());
+                    if (g == selectedGroup) {
+                        selectedGroup = null;
+                    } else {
+                        selectGroup(getGroup(e.getY()));
+                    }
+                } else {
+                    if (e.isMetaDown() || e.isControlDown()) {
+                        toggleTrackSelections(e);
+                    } else if (e.isShiftDown()) {
+                        shiftSelectTracks(e);
+                    } else if (!isTrackSelected(e)) {
+                        clearTrackSelections();
+                        selectTracks(e);
+                    }
+                }
+            } else {
+                if (isGrouped) {
+
                 } else if (!isTrackSelected(e)) {
                     clearTrackSelections();
                     selectTracks(e);
                 }
-            } else if (!isTrackSelected(e)) {
-                clearTrackSelections();
-                selectTracks(e);
             }
 
 
@@ -345,7 +436,17 @@ public class TrackNamePanel extends TrackSetComponent implements AdjustmentListe
                 glassPane.setVisible(false);
                 glassPane.setImage(null);
 
-                fireGhostDropEvent(new GhostDropEvent(dragStart, eventPoint));
+                fireGhostDropEvent(new GhostDropEvent(dragStart, eventPoint, dragTracks));
+
+                if (selectedGroup != null) {
+                    int idx = getGroupGapNumber(e.getY());
+                    TrackPanel dataTrackView = (TrackPanel) getParent();
+                    dataTrackView.moveGroup(selectedGroup, idx);
+                    dataTrackView.repaint();
+                }
+                selectedGroup = null;
+
+
             }
 
             if (e.isPopupTrigger()) {
@@ -360,10 +461,13 @@ public class TrackNamePanel extends TrackSetComponent implements AdjustmentListe
             }
 
             isDragging = false;
+            dragTracks.clear();
             dndImage = null;
 
+
         }
 
+
         public void mouseDragged(MouseEvent e) {
 
             Component c = e.getComponent();
@@ -371,7 +475,11 @@ public class TrackNamePanel extends TrackSetComponent implements AdjustmentListe
                 return;
             }
             if (!isDragging) {
-                dragStart = e.getPoint();
+
+                if(e.getPoint().distance(dragStart) < 5) {
+                    return;
+                }
+
                 dragStart.x = getWidth() / 2;
                 IGVMainFrame.getInstance().startDnD();
 
@@ -380,6 +488,16 @@ public class TrackNamePanel extends TrackSetComponent implements AdjustmentListe
                 }
                 IGVMainFrame.getInstance().getDnDGlassPane().setImage(dndImage);
                 isDragging = true;
+                dragTracks.clear();
+                dragTracks.addAll(IGVMainFrame.getInstance().getTrackManager().getSelectedTracks());
+
+
+                if (getGroups().size() > 0) {
+                    selectedGroup = getGroup(e.getY());
+                } else {
+                    selectedGroup = null;
+                }
+
                 // Code below paints target component on the dndImage.  It needs modified to paint some representation
                 // of the selectect tracks, probably the track names printed as a list.
             }
@@ -394,13 +512,14 @@ public class TrackNamePanel extends TrackSetComponent implements AdjustmentListe
 
                 glassPane.setPoint(p);
 
-                GuiUtilities.invokeOnEventThread(new Runnable() {
+                UIUtilities.invokeOnEventThread(new Runnable() {
 
                     public void run() {
-                        glassPane.paintImmediately(glassPane.getBounds());
+                        Rectangle bounds = new Rectangle(getBounds());
+                        bounds.height = 10000;
+                        glassPane.paintImmediately(bounds);
                     }
                 });
-                //glassPane.repaint();
             }
         }
 
@@ -418,73 +537,117 @@ public class TrackNamePanel extends TrackSetComponent implements AdjustmentListe
         }
     }
 
+
     class DropListener extends AbstractGhostDropManager {
 
         TrackNamePanel panel;
 
         public DropListener(TrackNamePanel target) {
             super(target);
-            this.panel = panel;
-
+            this.panel = target;
         }
 
         public void ghostDropped(GhostDropEvent e) {
             Point startPoint = e.getStartLocation();
             Point dropPoint = getTranslatedPoint(e.getDropLocation());
 
-            if (isInTarget(dropPoint)) {
-                tracksDropped(startPoint, dropPoint);
+
+            Rectangle bounds = component.getVisibleRect();
+            boolean isInTarget = dropPoint.y > bounds.y && dropPoint.y < bounds.getMaxY();
+
+            if (isInTarget) {
+                tracksDropped(startPoint, dropPoint, e.getTracks());
+                e.removeTracksFromSource();
+                e.setTracksDropped(true);
+            } else {
+                TrackPanel view = ((TrackPanel) getParent());
+                if (e.isTracksDropped()) {
+                    view.removeTracks(e.getTracks());
+                } else {
+                    // Defer removal until we are sure the tracks are dropped in another panel
+                    e.addSourcePanel(view);
+                }
             }
         }
-    }
 
-    void tracksDropped(Point startPoint, Point dropPoint) {
+        void tracksDropped(Point startPoint, Point dropPoint, List<Track> tracks) {
 
-        // This cast is horrid but we can't fix everything at once.
-        String viewType = ((TrackSetView) this.getParent()).getName();
-        List<MouseableRegion> regions = getMouseableRegions();
+            // This cast is horrid but we can't fix everything at once.
+            TrackPanel view = ((TrackPanel) getParent());
+            List<MouseableRegion> regions = getTrackRegions();
 
 
-        // Find the regions containing the startPoint and point
-        boolean before = true;
-        MouseableRegion dropRegion = null;
-        MouseableRegion startRegion = null;
-        for (MouseableRegion region : regions) {
-            if (region.containsPoint(dropPoint.x, dropPoint.y)) {
-                dropRegion = region;
-                Rectangle bnds = dropRegion.getBounds();
-                int dy1 = (dropPoint.y - bnds.y);
-                int dy2 = bnds.height - dy1;
-                before = dy1 < dy2;
-            }
-            if (region.containsPoint(startPoint.x, startPoint.y)) {
-                startRegion = region;
+            // Find the regions containing the startPoint and point
+            boolean before = true;
+            MouseableRegion dropRegion = null;
+            MouseableRegion startRegion = null;
+            for (MouseableRegion region : regions) {
+                if (region.containsPoint(dropPoint.x, dropPoint.y)) {
+                    dropRegion = region;
+                    Rectangle bnds = dropRegion.getBounds();
+                    int dy1 = (dropPoint.y - bnds.y);
+                    int dy2 = bnds.height - dy1;
+                    before = dy1 < dy2;
+                }
+                if (region.containsPoint(startPoint.x, startPoint.y)) {
+                    startRegion = region;
+                }
+                if (dropRegion != null && startRegion != null) {
+                    break;
+                }
             }
-            if (dropRegion != null && startRegion != null) {
-                break;
+
+
+            Track dropTrack = null;
+            if (dropRegion != null) {
+                Iterator<Track> tmp = dropRegion.getTracks().iterator();
+                if (tmp.hasNext()) {
+                    dropTrack = tmp.next();
+                }
             }
+            view.moveSelectedTracksTo(tracks, dropTrack, before);
+
+            IGVMainFrame.getInstance().doResizeTrackPanels();
+
         }
+    }
 
-        // If the dropRegion is not found,  or the dropRegion & startRegions are equal return
-        // DISABLED
-        //if (dropRegion == null || (startRegion != null && (startRegion == dropRegion))) {
-        //    return;
-        //}
-
-        // Get the track, the interface for getTracks returns a set but in the
-        // name panel (here) there is always one
-        if (dropRegion == null) {
-            TrackManager.getInstance().moveSelectedTracksTo(getSelectedTracks(),
-                    viewType, null, before);
-        } else {
-            Iterator<Track> tmp = dropRegion.getTracks().iterator();
-            if (tmp.hasNext()) {
-                Track dropTrack = tmp.next();
-                TrackManager.getInstance().moveSelectedTracksTo(
-                        getSelectedTracks(), viewType, dropTrack, before);
-            }
+    private void selectGroup(TrackGroup group) {
+        selectedGroup = group;
+        for (Track t : selectedGroup.getTracks()) {
+            t.setSelected(true);
+        }
+    }
+
+
+    class GroupExtent {
+        TrackGroup group;
+        int minY;
+        int maxY;
+
+        GroupExtent(TrackGroup group, int minY, int maxY) {
+            this.group = group;
+            this.maxY = maxY;
+            this.minY = minY;
         }
-        IGVMainFrame.getInstance().doResizeTrackPanels();
 
+        boolean contains(int y) {
+            return y > minY && y <= maxY;
+        }
+
+        boolean isAfter(int y) {
+            return minY > y;
+        }
     }
+
+    int getGroupGapNumber(int y) {
+        for (int i = 0; i < groupExtents.size(); i++) {
+            if (groupExtents.get(i).isAfter(y)) {
+                return i;
+            }
+        }
+        return groupExtents.size();
+    }
+
+
 }
diff --git a/src/org/broad/igv/ui/panel/TrackPanel.java b/src/org/broad/igv/ui/panel/TrackPanel.java
new file mode 100644
index 0000000..fd766fe
--- /dev/null
+++ b/src/org/broad/igv/ui/panel/TrackPanel.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+package org.broad.igv.ui.panel;
+
+
+import org.broad.igv.PreferenceManager;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.track.RegionScoreType;
+import org.broad.igv.track.Track;
+import org.broad.igv.track.TrackGroup;
+import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.RegionOfInterest;
+import org.broad.igv.ui.UIConstants;
+import org.netbeans.lib.awtextra.AbsoluteConstraints;
+import org.netbeans.lib.awtextra.AbsoluteLayout;
+
+import java.util.*;
+
+/**
+ * @author eflakes
+ */
+public class TrackPanel extends IGVPanel {
+
+    public static int X_SPACING_BETWEEN_COMPONENTS = 10;
+
+
+    private String name = null;
+    private TrackNamePanel namePanel;
+    private AttributePanel attributePanel;
+    private DataPanel dataPanel;
+    private String groupAttribute;
+    int trackCountEstimate = 0;  // <= used to size array list, not neccesarily precise
+
+    /**
+     * Map of attribute name -> associated track group
+     */
+    List<TrackGroup> trackGroups;
+
+    /**
+     * Constructs ...
+     *
+     * @param name
+     */
+    public TrackPanel(String name) {
+        this.name = name;
+        TrackGroup nullGroup = new TrackGroup();
+        nullGroup.setDrawBorder(false);
+        trackGroups = new LinkedList();
+        trackGroups.add(nullGroup);
+        init();
+    }
+
+
+    private void init() {
+        setLayout(new AbsoluteLayout());
+        namePanel = new TrackNamePanel(this);
+        attributePanel = new AttributePanel(this);
+        dataPanel = new DataPanel(this);
+        add(namePanel, new AbsoluteConstraints(0, 0, 150, 230));
+        add(attributePanel, new AbsoluteConstraints(160, 0, 120, 230));
+        add(dataPanel, new AbsoluteConstraints(290, 0, 710, 230));
+    }
+
+
+    /**
+     * @return the namePanel
+     */
+    public TrackNamePanel getNamePanel() {
+        return namePanel;
+    }
+
+    /**
+     * @return the attributePanel
+     */
+    public AttributePanel getAttributePanel() {
+        return attributePanel;
+    }
+
+    /**
+     * @return the dataPanel
+     */
+    public DataPanel getDataPanel() {
+        return dataPanel;
+    }
+
+
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public List<TrackGroup> getGroups() {
+        return trackGroups;
+    }
+
+    /**
+     * Method description
+     *
+     * @return
+     */
+    public boolean hasTracks() {
+        for (TrackGroup tg : trackGroups) {
+            if (tg.getTracks().size() > 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public int getVisibleTrackCount() {
+        int count = 0;
+        for (TrackGroup tg : trackGroups) {
+            for (Track t : tg.getTracks()) {
+                if (t.isVisible()) {
+                    count++;
+                }
+            }
+        }
+        return count;
+    }
+
+
+    public List<Track> getTracks() {
+        ArrayList<Track> tracks = new ArrayList(trackCountEstimate);
+        for (TrackGroup tg : trackGroups) {
+            tracks.addAll(tg.getTracks());
+        }
+        return tracks;
+    }
+
+    public void clearTracks() {
+        trackGroups.clear();
+        trackCountEstimate = 0;
+    }
+
+    /**
+     * Add a track to this panel.  If tracks are grouped, search for correct group, or make a new one if not found.
+     *
+     * @param track
+     */
+    public void addTrack(Track track) {
+
+        String groupName = (groupAttribute == null ? null : track.getAttributeValue(groupAttribute));
+        boolean foundGroup = false;
+        for (TrackGroup tg : trackGroups) {
+            if (groupAttribute == null || groupName == null || tg.getName().equals(groupName)) {
+                tg.add(track);
+                foundGroup = true;
+                break;
+            }
+        }
+        if (!foundGroup) {
+            TrackGroup newGroup = new TrackGroup(groupName);
+            newGroup.add(track);
+            if (groupAttribute == null) {
+                newGroup.setDrawBorder(false);
+            }
+            trackGroups.add(newGroup);
+        }
+        trackCountEstimate++;
+    }
+
+    public void addTracks(Collection<Track> tracks) {
+        for (Track t : tracks) {
+            addTrack(t);
+        }
+    }
+
+    public void moveGroup(TrackGroup group, int index) {
+
+        if(index > trackGroups.indexOf(group)) {
+            index--;
+        }
+        trackGroups.remove(group);
+        if (index >= trackGroups.size()) {
+           trackGroups.add(group);
+        } else {
+            trackGroups.add(index, group);
+        }
+        ;
+    }
+
+
+    public void reset() {
+        this.groupAttribute = null;
+        trackGroups.clear();
+    }
+
+    /**
+     * Rebuild group list for supplied attribute.
+     *
+     * @param attribute
+     */
+    public void groupTracksByAttribute(String attribute) {
+
+        this.groupAttribute = attribute;
+        List<Track> tracks = getTracks();
+        trackGroups.clear();
+
+        if (attribute == null || attribute.length() == 0) {
+            TrackGroup nullGroup = new TrackGroup();
+            nullGroup.addAll(tracks);
+            nullGroup.setDrawBorder(false);
+            trackGroups.add(nullGroup);
+        } else {
+            Map<String, TrackGroup> groupMap = new HashMap();
+            for (Track track : tracks) {
+                String attributeValue = track.getAttributeValue(attribute);
+
+                if (attributeValue == null) {
+                    attributeValue = "";
+                }
+
+                TrackGroup group = groupMap.get(attributeValue);
+
+                if (group == null) {
+                    group = new TrackGroup(attributeValue);
+                    groupMap.put(attributeValue, group);
+                    trackGroups.add(group);
+                }
+                group.add(track);
+            }
+        }
+    }
+
+    public void sortTracksByAttributes(final String attributeNames[], final boolean[] ascending) {
+
+        assert attributeNames.length == ascending.length;
+
+        for (TrackGroup tg : trackGroups) {
+            tg.sortByAttributes(attributeNames, ascending);
+        }
+    }
+
+
+    public void sortTracksByPosition(List<String> trackIds) {
+        for (TrackGroup tg : trackGroups) {
+            tg.sortByList(trackIds);
+        }
+
+    }
+
+
+    /**
+     * Sort all groups (data and feature) by a computed score over a region.  The
+     * sort is done twice (1) groups are sorted with the featureGroup, and (2) the
+     * groups themselves are sorted.
+     *
+     * @param region
+     * @param type
+     */
+    public void sortByRegionsScore(final RegionOfInterest region, final RegionScoreType type) {
+
+        boolean useLinkedSorting = PreferenceManager.getInstance().isLinkedSortingEnabled();
+        String linkingAtt = IGVMainFrame.getInstance().getSession().getOverlayAttribute();
+
+        sortGroupsByRegionScore(trackGroups, region, type);
+
+        for (TrackGroup group : trackGroups) {
+            // If there is a non-null linking attribute
+            // Segregate tracks into 2 sub-groups, those matching the score type and those that do not
+            if (linkingAtt != null && !useLinkedSorting) {
+                group.sortByRegionScore(region, type);
+            } else {
+                group.sortGroup(region, linkingAtt, type);
+            }
+
+        }
+    }
+
+    /**
+     * Sort groups by a score (not the tracks within the group).
+     *
+     * @param groups
+     * @param region
+     * @param type
+     */
+    private void sortGroupsByRegionScore(List<TrackGroup> groups,
+                                         final RegionOfInterest region,
+                                         final RegionScoreType type) {
+        if ((groups != null) && (region != null) && !groups.isEmpty()) {
+            final int zoom = Math.max(0, ViewContext.getInstance().getZoom());
+            final String chr = region.getChr();
+            final int start = region.getStart();
+            final int end = region.getEnd();
+            Comparator<TrackGroup> c = new Comparator<TrackGroup>() {
+
+                public int compare(TrackGroup group1, TrackGroup group2) {
+                    float s1 = group1.getRegionScore(chr, start, end, zoom, type);
+                    float s2 = group2.getRegionScore(chr, start, end, zoom, type);
+
+                    if (s2 > s1) {
+                        return 1;
+                    } else if (s1 < s2) {
+                        return -1;
+                    } else {
+                        return 0;
+                    }
+
+                }
+            };
+
+            Collections.sort(groups, c);
+        }
+
+    }
+
+
+    /**
+     * Replace current gene track with new one.  This is called upon switching genomes.
+     *
+     * @param newGeneTrack
+     * @return true if gene track is found.
+     */
+    public boolean setGeneTrack(Track geneTrack, Track newGeneTrack) {
+
+        boolean foundGeneTrack = false;
+
+        for (TrackGroup g : trackGroups) {
+            if (g.contains(geneTrack)) {
+                int geneTrackIndex = g.indexOf(geneTrack);
+                g.remove(geneTrack);
+                geneTrackIndex = Math.min(g.size(), geneTrackIndex);
+                g.add(geneTrackIndex, newGeneTrack);
+                foundGeneTrack = true;
+            }
+        }
+
+
+        return foundGeneTrack;
+
+    }
+
+    public void removeTracks(Collection<Track> tracksToRemove) {
+        for (TrackGroup tg : trackGroups) {
+            tg.removeTracks(tracksToRemove);
+        }
+    }
+
+
+    /**
+     * Insert the selectedTracks collection either before or after the target and return true.
+     *
+     * @param selectedTracks
+     * @param targetTrack
+     * @param before
+     */
+    public void moveSelectedTracksTo(Collection<Track> selectedTracks,
+                                     Track targetTrack,
+                                     boolean before) {
+
+        if (selectedTracks.isEmpty()) {
+            return;
+        }
+
+        for (TrackGroup tg : trackGroups) {
+            if (tg.moveSelectedTracksTo(selectedTracks, targetTrack, before)) {
+                return;
+            }
+        }
+    }
+
+
+    public int getPreferredPanelHeight() {
+        int height = 0;
+
+        // Copy to prevent concurrent modification exception.  Should we synchronize on the
+        // groups collection instead?
+        Collection<TrackGroup> groups = getGroups();
+
+        if (groups.size() > 1) {
+            height += UIConstants.groupGap;
+        }
+
+        for (Iterator<TrackGroup> groupIter = groups.iterator(); groupIter.hasNext();) {
+            TrackGroup group = groupIter.next();
+            if (group != null && group.isVisible()) {
+                if (groups.size() > 1) {
+                    height += UIConstants.groupGap;
+                }
+                height += group.getPreferredHeight();
+            }
+        }
+
+        return Math.max(20, height);
+    }
+}
diff --git a/src/org/broad/igv/ui/panel/TrackPanelComponent.java b/src/org/broad/igv/ui/panel/TrackPanelComponent.java
new file mode 100644
index 0000000..1d69649
--- /dev/null
+++ b/src/org/broad/igv/ui/panel/TrackPanelComponent.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.ui.panel;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import org.apache.log4j.Logger;
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.track.Track;
+import org.broad.igv.track.TrackMenuUtils;
+import org.broad.igv.ui.IGVMainFrame;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author eflakes
+ */
+abstract public class TrackPanelComponent extends JPanel {
+
+    private static Logger log = Logger.getLogger(TrackPanelComponent.class);
+    List<MouseableRegion> trackRegions;
+
+    private TrackPanel trackPanel;
+
+
+    public TrackPanelComponent(TrackPanel trackPanel) {
+        this.trackPanel = trackPanel;
+        setFocusable(true);
+        trackRegions = new ArrayList();
+    }
+
+    public TrackPanel getTrackSetView() {
+        if (trackPanel == null) {
+            trackPanel = (TrackPanel) getParent();
+        }
+        return trackPanel;
+    }
+
+    public String getTrackSetID() {
+        return getTrackSetView().getName();
+    }
+
+
+    public int calculateResizeHeight() {
+
+        // Get the TrackPanel that holds the track name,
+        // attribute and data panels
+        TrackPanel trackView = getTrackSetView();
+
+        // No size if no IGVPanel yet
+        if (trackView == null) {
+            return 0;
+        }
+
+        // Get the viewport height
+        int viewportHeight = trackView.getViewportHeight();
+
+        // Calculate new height
+        if (trackView.hasTracks()) {
+
+            // The sum of all the track heights
+            int newHeight = trackView.getPreferredPanelHeight();
+
+            // Make sure the new  height is never less than
+            // the height of the actual viewport view
+            newHeight = (newHeight < viewportHeight) ? viewportHeight : newHeight;
+
+            return newHeight;
+
+        } else {
+
+            // Resize parent container
+            return viewportHeight;
+        }
+    }
+
+
+    @Override
+    public void setBounds(int x, int y, int width, int height) {
+
+        int adjustedHeight = calculateResizeHeight();
+        super.setBounds(x, y, width, adjustedHeight);
+    }
+
+    @Override
+    public void setBounds(Rectangle r) {
+
+        int adjustedHeight = calculateResizeHeight();
+        super.setBounds(r.x, r.y, r.width, adjustedHeight);
+    }
+
+
+    @Override
+    public void setSize(int width, int height) {
+
+        int adjustedHeight = calculateResizeHeight();
+        super.setSize(width, adjustedHeight);
+    }
+
+    @Override
+    public void setSize(Dimension d) {
+
+        int adjustedHeight = calculateResizeHeight();
+        super.setSize(new Dimension(d.width, adjustedHeight));
+    }
+
+    protected void addMousableRegion(MouseableRegion region) {
+        trackRegions.add(region);
+    }
+
+    protected void removeMousableRegions() {
+        trackRegions.clear();
+    }
+
+    protected List<MouseableRegion> getTrackRegions() {
+        return trackRegions;
+    }
+
+    public boolean scrollTo(String trackName) {
+        Track t = findNextTrackMatching(trackName);
+        if (t != null) {
+            IGVMainFrame.getInstance().getTrackManager().clearSelections();
+            t.setSelected(true);
+            if (trackPanel.getScrollPane().getVerticalScrollBar().isShowing()) {
+                trackPanel.getScrollPane().getVerticalScrollBar().setValue(t.getTop());
+            }
+            return true;
+        }
+
+        return false;
+    }
+
+    int searchIdx = 0;
+
+    private synchronized Track findNextTrackMatching(String trackName) {
+        List<Track> tracks = getAllTracks();
+        searchIdx = Math.min(searchIdx, tracks.size());
+        for (int i = searchIdx; i < tracks.size(); i++) {
+            Track t = tracks.get(i);
+            if (t.getName().toUpperCase().contains(trackName.toUpperCase())) {
+                searchIdx = i + 1;
+                return t;
+            }
+        }
+        for (int i = 0; i < searchIdx; i++) {
+            Track t = tracks.get(i);
+            if (t.getName().toUpperCase().contains(trackName.toUpperCase())) {
+                searchIdx = i + 1;
+                return t;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Method description
+     *
+     * @param x
+     * @param y
+     * @return
+     */
+    abstract public String getPopupMenuTitle(int x, int y);
+
+    protected Collection<Track> getSelectedTracks() {
+        return IGVMainFrame.getInstance().getTrackManager().getSelectedTracks();
+    }
+
+    public List<Track> getAllTracks() {
+        TrackPanel dataTrackView = (TrackPanel) getParent();
+        return dataTrackView.getTracks();
+    }
+
+
+    protected ViewContext getViewContext() {
+        return ViewContext.getInstance();
+    }
+
+    protected void openPopupMenu(final MouseEvent e) {
+
+
+        // If there is a single selected track give it a chance to handle the click
+        Collection<Track> selectedTracs = getSelectedTracks();
+        
+        if (selectedTracs.size() == 0) {
+            return;
+        }
+
+        if (selectedTracs.size() == 1) {
+            if (selectedTracs.iterator().next().handleClick(e)) {
+                return;
+            }
+        }
+        String title = getPopupMenuTitle(e.getX(), e.getY());
+
+        JPopupMenu menu = TrackMenuUtils.getPopupMenu(selectedTracs, title, e);
+
+        menu.show(e.getComponent(), e.getX(), e.getY());
+    }
+
+    protected void toggleTrackSelections(MouseEvent e) {
+        for (MouseableRegion mouseRegion : trackRegions) {
+            if (mouseRegion.containsPoint(e.getX(), e.getY())) {
+                IGVMainFrame.getInstance().getTrackManager().toggleTrackSelections(mouseRegion.getTracks());
+                return;
+            }
+        }
+    }
+
+    protected void clearTrackSelections() {
+        IGVMainFrame.getInstance().getTrackManager().clearSelections();
+    }
+
+    protected void selectTracks(MouseEvent e) {
+
+        if (log.isDebugEnabled()) {
+            log.debug("Enter selectTracks");
+        }
+
+
+
+        for (MouseableRegion mouseRegion : trackRegions) {
+            if (mouseRegion.containsPoint(e.getX(), e.getY())) {
+                IGVMainFrame.getInstance().getTrackManager().setTrackSelections(mouseRegion.getTracks());
+                return;
+            }
+        }
+    }
+
+    protected boolean isTrackSelected(MouseEvent e) {
+        for (MouseableRegion mouseRegion : trackRegions) {
+            if (mouseRegion.containsPoint(e.getX(), e.getY())) {
+                for (Track t : mouseRegion.getTracks()) {
+                    if (t.isSelected()) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/src/org/broad/igv/ui/panel/TrackPanelScrollPane.java b/src/org/broad/igv/ui/panel/TrackPanelScrollPane.java
new file mode 100644
index 0000000..5dd6d5d
--- /dev/null
+++ b/src/org/broad/igv/ui/panel/TrackPanelScrollPane.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.igv.ui.panel;
+
+import com.jidesoft.swing.JideScrollPane;
+import org.broad.igv.track.TrackManager;
+import org.broad.igv.ui.IGVMainFrame;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * @author jrobinso
+ */
+public class TrackPanelScrollPane extends JideScrollPane {
+
+    TrackPanel trackPanel;
+
+    @Override
+    public void setViewportView(Component trackSetView) {
+        if (!(trackSetView instanceof TrackPanel)) {
+            throw new IllegalArgumentException("Class TrackPanelScrollPane can only contain a TrackPanel");
+        }
+        super.setViewportView(trackSetView);
+        this.trackPanel = (TrackPanel) trackSetView;
+        this.verticalScrollBar.addAdjustmentListener(getNamePanel());
+        init();
+    }
+
+    public TrackPanel getTrackPanel() {
+        return trackPanel;
+    }
+
+    @Override
+    public String getName() {
+        return trackPanel.getName();
+    }
+
+    public void minimizeHeight() {
+        int prefHeight = trackPanel.getPreferredPanelHeight();
+        if (prefHeight < trackPanel.getViewportHeight()) {
+            this.setSize(getWidth(), prefHeight);
+        }
+    }
+
+    public DataPanel getDataPanel() {
+        return trackPanel.getDataPanel();
+    }
+
+    public boolean isEmpty() {
+        return !trackPanel.hasTracks();
+    }
+
+    public TrackNamePanel getNamePanel() {
+        return trackPanel.getNamePanel();
+    }
+
+    public AttributePanel getAttributePanel() {
+        return trackPanel.getAttributePanel();
+    }
+
+    public Dimension getPreferredSize() {
+        //if (!tracksAreLoaded()) {
+            return super.getPreferredSize();
+        //} else {
+        //    int prefWidth = super.getPreferredSize().width;
+        //    int prefHeight = trackPanel.getPreferredPanelHeight();
+        //    return new Dimension(prefWidth, prefHeight);
+        //}
+    }
+
+    /**
+     * TODO -- a hack, needed to override pref size on initial startup.
+     *
+     * @return
+     */
+    private boolean tracksAreLoaded() {
+        return IGVMainFrame.getInstance().getTrackManager().getAllTracks(false).size() > 0;
+    }
+
+    private void init() {
+        setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(102, 102, 102)));
+        setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+        setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
+        setFocusTraversalPolicyProvider(true);
+        setMinimumSize(new java.awt.Dimension(0, 0));
+        setOpaque(false);
+    }
+}
diff --git a/src/org/broad/igv/ui/panel/TrackSetComponent.java b/src/org/broad/igv/ui/panel/TrackSetComponent.java
deleted file mode 100644
index 3898f3c..0000000
--- a/src/org/broad/igv/ui/panel/TrackSetComponent.java
+++ /dev/null
@@ -1,350 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.ui.panel;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
-import org.broad.igv.track.Track;
-import org.broad.igv.track.TrackManager;
-import org.broad.igv.track.TrackMenuUtils;
-import org.broad.igv.ui.IGVModel;
-import org.broad.igv.ui.ViewContext;
-
-import javax.swing.*;
-import java.awt.*;
-import java.awt.event.MouseEvent;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * @author eflakes
- */
-abstract public class TrackSetComponent extends JPanel {
-
-    private static Logger log = Logger.getLogger(TrackSetComponent.class);
-    final static public String MULTILEVEL_FEATURE_EVENT = "MULTILEVEL_FEATURE_EVENT";
-    private boolean trackSelectionEnabled = true;
-    List<MouseableRegion> mouseableRegions;
-    boolean isDragging = false;
-    int dragX, dragY;
-    Track dropTarget = null;
-    TrackSetView trackSetView;
-
-    /**
-     * Constructs ...
-     */
-    public TrackSetComponent() {
-        initialize();
-        setFocusable(true);
-        mouseableRegions = new ArrayList();
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public TrackSetView getTrackSetView() {
-        if (trackSetView == null) {
-            trackSetView = (TrackSetView) getParent();
-        }
-        return trackSetView;
-    }
-
-    public String getTrackSetID() {
-        return getTrackSetView().getName();
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public int calculateResizeHeight() {
-
-        // Get the TrackSetView that holds the track name,
-        // attribute and data panels
-        TrackSetView trackView = getTrackSetView();
-
-        // No size if no View yet
-        if (trackView == null) {
-            return 0;
-        }
-
-        // Get the viewport height
-        int viewportHeight = trackView.getViewportHeight();
-
-        // Calculate new height
-        if (trackView.hasTracks()) {
-
-            // The sum of all the track heights
-            int preferredTrackViewHeight = trackView.getPreferredPanelHeight();
-
-            // Make sure the preferred height is never less than
-            // the height of the actual viewport view
-            preferredTrackViewHeight = (preferredTrackViewHeight < viewportHeight)
-                    ? viewportHeight : preferredTrackViewHeight;
-
-            // Resize all track set view's components
-            return preferredTrackViewHeight;
-
-        } else {
-
-            // Resize parent container
-            return viewportHeight;
-        }
-    }
-
-    /**
-     * Method description
-     *
-     * @param x
-     * @param y
-     * @param width
-     * @param height
-     */
-    @Override
-    public void setBounds(int x, int y, int width, int height) {
-
-        int adjustedHeight = calculateResizeHeight();
-        super.setBounds(x, y, width, adjustedHeight);
-    }
-
-    /**
-     * Method description
-     *
-     * @param r
-     */
-    @Override
-    public void setBounds(Rectangle r) {
-
-        int adjustedHeight = calculateResizeHeight();
-        super.setBounds(r.x, r.y, r.width, adjustedHeight);
-    }
-
-    /**
-     * Method description
-     *
-     * @param width
-     * @param height
-     */
-    @Override
-    public void setSize(int width, int height) {
-
-        int adjustedHeight = calculateResizeHeight();
-        super.setSize(width, adjustedHeight);
-    }
-
-    /**
-     * Method description
-     *
-     * @param d
-     */
-    @Override
-    public void setSize(Dimension d) {
-
-        int adjustedHeight = calculateResizeHeight();
-        super.setSize(new Dimension(d.width, adjustedHeight));
-    }
-
-    protected void addMousableRegion(MouseableRegion region) {
-        mouseableRegions.add(region);
-    }
-
-    protected void removeMousableRegions() {
-        mouseableRegions.clear();
-    }
-
-    protected List<MouseableRegion> getMouseableRegions() {
-        return mouseableRegions;
-    }
-
-    public boolean scrollTo(String trackName) {
-        Track t = findNextTrackMatching(trackName);
-        if (t != null) {
-            TrackManager.getInstance().clearSelections();
-            t.setSelected(true);
-            if (trackSetView.getScrollPane().getVerticalScrollBar().isShowing()) {
-                trackSetView.getScrollPane().getVerticalScrollBar().setValue(t.getTop());
-            }
-            return true;
-        }
-
-        return false;
-    }
-
-    int searchIdx = 0;
-
-    private synchronized Track findNextTrackMatching(String trackName) {
-        List<Track> tracks = getAllTracks();
-        searchIdx = Math.min(searchIdx, tracks.size());
-        for (int i = searchIdx; i < tracks.size(); i++) {
-            Track t = tracks.get(i);
-            if (t.getDisplayName().toUpperCase().contains(trackName.toUpperCase())) {
-                searchIdx = i + 1;
-                return t;
-            }
-        }
-        for (int i = 0; i < searchIdx; i++) {
-            Track t = tracks.get(i);
-            if (t.getDisplayName().toUpperCase().contains(trackName.toUpperCase())) {
-                searchIdx = i + 1;
-                return t;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Method description
-     *
-     * @param x
-     * @param y
-     * @return
-     */
-    abstract public String getPopupMenuTitle(int x, int y);
-
-    protected Collection<Track> getSelectedTracks() {
-        if (log.isDebugEnabled()) {
-            log.debug("Enter getSelectedTracks");
-        }
-        return TrackManager.getInstance().getSelectedTracks();
-    }
-
-    public List<Track> getAllTracks() {
-        TrackSetView dataTrackView = (TrackSetView) getParent();
-        return dataTrackView.getAllTracks();
-    }
-
-    boolean isZoomEnabled() {
-        return !getViewContext().getChrName().equals(IGVConstants.CHR_ALL);
-    }
-
-    /**
-     * Initial Setup for the component is below
-     */
-    private void initialize() {
-
-        //MouseInputAdapter mouseAdapter = new TrackPanelAdapter(this);
-
-        //addMouseMotionListener(mouseAdapter);
-        //addMouseListener(mouseAdapter);
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public boolean isTrackSelectionEnabled() {
-        return trackSelectionEnabled;
-    }
-
-    /**
-     * Method description
-     *
-     * @param trackSelectionEnabled
-     */
-    public void setTrackSelectionEnabled(boolean trackSelectionEnabled) {
-        this.trackSelectionEnabled = trackSelectionEnabled;
-    }
-
-    /**
-     * //TODO reimplement this without referencing IGVMainFrame.getInstance().
-     *
-     * @return
-     */
-    public ViewContext getViewContext() {
-        return IGVModel.getInstance().getViewContext();
-    }
-
-    protected void openPopupMenu(final MouseEvent e) {
-
-        if (log.isDebugEnabled()) {
-            log.debug("Enter openPopupMenu");
-        }
-
-        // If there is a single selected track give it a chance to handle the click
-        Collection<Track> selectedTracs = getSelectedTracks();
-        if (selectedTracs.size() == 0) {
-            return;
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug("calling handeClick");
-        }
-
-        if (selectedTracs.size() == 1) {
-            if (selectedTracs.iterator().next().handleClick(e)) {
-                return;
-            }
-        }
-        String title = getPopupMenuTitle(e.getX(), e.getY());
-
-        TrackMenuUtils.getPopupMenu(selectedTracs, e.getX(), e.getY(), title).show(e.getComponent(),
-                e.getX(), e.getY());
-    }
-
-    protected void toggleTrackSelections(MouseEvent e) {
-        for (MouseableRegion mouseRegion : mouseableRegions) {
-            if (mouseRegion.containsPoint(e.getX(), e.getY())) {
-                TrackManager.getInstance().toggleTrackSelections(mouseRegion.getTracks());
-                return;
-            }
-        }
-    }
-
-    protected void clearTrackSelections() {
-        TrackManager.getInstance().clearSelections();
-    }
-
-    protected void selectTracks(MouseEvent e) {
-
-        if (log.isDebugEnabled()) {
-            log.debug("Enter selectTracks");
-        }
-
-        for (MouseableRegion mouseRegion : mouseableRegions) {
-            if (mouseRegion.containsPoint(e.getX(), e.getY())) {
-                TrackManager.getInstance().setTrackSelections(mouseRegion.getTracks());
-                return;
-            }
-        }
-    }
-
-    protected boolean isTrackSelected(MouseEvent e) {
-        for (MouseableRegion mouseRegion : mouseableRegions) {
-            if (mouseRegion.containsPoint(e.getX(), e.getY())) {
-                for (Track t : mouseRegion.getTracks()) {
-                    if (t.isSelected()) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-
-    }
-}
diff --git a/src/org/broad/igv/ui/panel/TrackSetScrollPane.java b/src/org/broad/igv/ui/panel/TrackSetScrollPane.java
deleted file mode 100644
index de767bf..0000000
--- a/src/org/broad/igv/ui/panel/TrackSetScrollPane.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.ui.panel;
-
-import com.jidesoft.swing.JideScrollPane;
-
-import javax.swing.*;
-import java.awt.*;
-
-/**
- * @author jrobinso
- */
-public class TrackSetScrollPane extends JideScrollPane {
-
-    TrackSetView trackSetView;
-
-    @Override
-    public void setViewportView(Component trackSetView) {
-        if (!(trackSetView instanceof TrackSetView)) {
-            throw new IllegalArgumentException(
-                    "Class TrackSetScrollPane can only contain a TrackSetView");
-        }
-        super.setViewportView(trackSetView);
-        this.trackSetView = (TrackSetView) trackSetView;
-        this.verticalScrollBar.addAdjustmentListener(getNamePanel());
-        init();
-    }
-
-    public TrackSetView getTrackSetView() {
-        return trackSetView;
-    }
-
-    @Override
-    public String getName() {
-        return trackSetView.getName();
-    }
-
-    public void minimizeHeight() {
-        int prefHeight = trackSetView.getPreferredPanelHeight();
-        if (prefHeight < trackSetView.getViewportHeight()) {
-            this.setSize(getWidth(), prefHeight);
-        }
-    }
-
-    public DataPanel getDataPanel() {
-        return trackSetView.getDataPanel();
-    }
-
-    public boolean isEmpty() {
-        return trackSetView.getAllTracks().isEmpty();
-    }
-
-    public TrackNamePanel getNamePanel() {
-        return trackSetView.getNamePanel();
-    }
-
-    public AttributePanel getAttributePanel() {
-        return trackSetView.getAttributePanel();
-    }
-
-    private void init() {
-        setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(102, 102, 102)));
-        setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
-        setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
-        setFocusTraversalPolicyProvider(true);
-        setMinimumSize(new java.awt.Dimension(0, 0));
-        setOpaque(false);
-    }
-}
diff --git a/src/org/broad/igv/ui/panel/TrackSetView.java b/src/org/broad/igv/ui/panel/TrackSetView.java
deleted file mode 100644
index ee0ee43..0000000
--- a/src/org/broad/igv/ui/panel/TrackSetView.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-package org.broad.igv.ui.panel;
-
-//~--- non-JDK imports --------------------------------------------------------
-
-import org.broad.igv.IGVConstants;
-import org.broad.igv.track.Track;
-import org.broad.igv.track.TrackGroup;
-import org.broad.igv.track.TrackManager;
-import org.netbeans.lib.awtextra.AbsoluteConstraints;
-import org.netbeans.lib.awtextra.AbsoluteLayout;
-
-import java.awt.*;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * @author eflakes
- */
-public class TrackSetView extends View {
-
-    private String name = null;
-    private TrackNamePanel namePanel;
-    private AttributePanel attributePanel;
-    private DataPanel dataPanel;
-
-    /**
-     * Constructs ...
-     *
-     * @param name
-     */
-    public TrackSetView(String name) {
-        this.name = name;
-        init();
-    }
-
-    /**
-     * This override is a "hack" to get around the netbeans visual editor's insistence on setting
-     * a layout after component initialization
-     *
-     * @param layout
-     */
-    @Override
-    public void setLayout(LayoutManager layout) {
-        if (layout != null) {
-            super.setLayout(layout);
-        }
-    }
-
-
-    private void init() {
-        setLayout(new AbsoluteLayout());
-        namePanel = new TrackNamePanel();
-        attributePanel = new AttributePanel();
-        dataPanel = new DataPanel();
-        add(namePanel, new AbsoluteConstraints(0, 0, 150, 230));
-        add(attributePanel, new AbsoluteConstraints(160, 0, 120, 230));
-        add(dataPanel, new AbsoluteConstraints(290, 0, 710, 230));
-    }
-
-    public String getName() {
-        return name;
-    }
-
-
-    protected List<Track> getAllTracks() {
-        return TrackManager.getInstance().getTracksForPanel(name);
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public Collection<TrackGroup> getGroups() {
-        return TrackManager.getInstance().getTrackGroups(name);
-    }
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public boolean hasTracks() {
-        return (!getAllTracks().isEmpty());
-    }
-
-
-    /**
-     * Method description
-     *
-     * @return
-     */
-    public int getPreferredPanelHeight() {
-        int height = 0;
-
-        // Copy to prevent concurrent modification exception.  Should we synchronize on the 
-        // groups collection instead?
-        Collection<TrackGroup> groups = getGroups();
-
-        if (groups.size() > 1) {
-            height += IGVConstants.groupGap;
-        }
-
-        for (Iterator<TrackGroup> groupIter = groups.iterator(); groupIter.hasNext();) {
-            TrackGroup group = groupIter.next();
-            if (group != null && group.isVisible()) {
-                if (groups.size() > 1) {
-                    height += IGVConstants.groupGap;
-                }
-                height += group.getPreferredHeight();
-            }
-        }
-
-        return Math.max(20, height);
-    }
-
-    /**
-     * @return the namePanel
-     */
-    public TrackNamePanel getNamePanel() {
-        return namePanel;
-    }
-
-    /**
-     * @return the attributePanel
-     */
-    public AttributePanel getAttributePanel() {
-        return attributePanel;
-    }
-
-    /**
-     * @return the dataPanel
-     */
-    public DataPanel getDataPanel() {
-        return dataPanel;
-    }
-}
diff --git a/src/org/broad/igv/ui/panel/View.java b/src/org/broad/igv/ui/panel/View.java
deleted file mode 100644
index ad00d8c..0000000
--- a/src/org/broad/igv/ui/panel/View.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package org.broad.igv.ui.panel;
-
-import org.broad.igv.ui.GuiUtilities;
-import org.broad.igv.ui.util.Packable;
-import org.broad.igv.ui.util.Tool;
-
-import javax.swing.*;
-import java.awt.*;
-
-/**
- * @author eflakes
- */
-public class View extends JPanel {
-
-    public static int X_SPACING_BETWEEN_COMPONENTS = 10;
-
-    private String viewName = "View";
-    private Tool tool;
-
-    public Tool getTool() {
-        return tool;
-    }
-
-    public void setTool(Tool tool) {
-        this.tool = tool;
-    }
-
-    public int getViewportHeight() {
-
-        int height = 0;
-        Container parent = getParent();
-
-        if (parent instanceof JViewport) {
-            height = ((JViewport) parent).getHeight();
-        } else {
-            height = parent.getHeight();
-        }
-
-        return height;
-    }
-
-    /**
-     * Scrollpane height
-     *
-     * @return
-     */
-    public int getScrollPaneHeight() {
-
-        int height = 0;
-        Container parent = getParent();
-
-        if (parent instanceof JViewport) {
-            height = ((JViewport) parent).getParent().getHeight();
-        } else {
-            height = parent.getHeight();
-        }
-
-        return height;
-    }
-
-    public int getViewportWidth() {
-
-        int width = 0;
-        Container parent = getParent();
-
-        if (parent instanceof JViewport) {
-            width = (int) ((JViewport) parent).getViewSize().getWidth();
-        } else {
-            width = parent.getWidth();
-        }
-
-        return width;
-    }
-
-    public int getScrollPaneWidth() {
-
-        int width = 0;
-        Container parent = getParent();
-
-        if (parent instanceof JViewport) {
-            width = (int) ((JViewport) parent).getParent().getWidth();
-        } else {
-            width = parent.getWidth();
-        }
-
-        return width;
-    }
-
-    public JViewport getViewport() {
-
-        JViewport viewport = null;
-        Container parent = getParent();
-
-        if (parent instanceof JViewport) {
-            viewport = ((JViewport) parent);
-        }
-        return viewport;
-    }
-
-    public JScrollPane getScrollPane() {
-
-        JScrollPane scollpane = null;
-        Container parent = getParent();
-
-        if (parent instanceof JViewport) {
-            scollpane = (JScrollPane) ((JViewport) parent).getParent();
-        }
-        return scollpane;
-    }
-
-    public void setHeight(int height) {
-
-        if (height < 0) {
-            height = 0;
-        }
-
-        // First resize itself
-        final Dimension dimension = new Dimension(getWidth(), height);
-        setSize(dimension);
-        setPreferredSize(dimension);
-
-        // Then resize its children
-        Component[] children = getComponents();
-        for (final Component child : children) {
-
-            final Dimension childDimension =
-                    new Dimension(child.getWidth(), height);
-
-            GuiUtilities.invokeOnEventThread(new Runnable() {
-
-                public void run() {
-                    child.setSize(childDimension);
-                    child.setPreferredSize(childDimension);
-                }
-            });
-        }
-    }
-
-    public String getViewName() {
-        return viewName;
-    }
-
-    public void setViewName(String viewName) {
-        this.viewName = viewName;
-    }
-
-    public void packView() {
-
-        GuiUtilities.invokeOnEventThread(new Runnable() {
-
-            public void run() {
-
-                Integer attributePanelEnd = null;
-                int width = 0;
-
-                // Pack the attribute panels
-                Component[] children = getComponents();
-                for (final Component child : children) {
-                    if (child instanceof Packable) {
-                        ((Packable) child).packComponent();
-
-                        if (attributePanelEnd == null) {
-                            int x = child.getX();
-                            width = child.getWidth();
-                            attributePanelEnd = new Integer(x + width);
-                        }
-                    }
-                }
-
-                // Change Track and Header panel X location
-                if (attributePanelEnd != null) {
-
-                    children = getComponents();
-                    for (final Component child : children) {
-
-                        if (child instanceof DataPanel ||
-                                child instanceof HeaderPanel) {
-
-                            if (width > 0) {
-                                attributePanelEnd += X_SPACING_BETWEEN_COMPONENTS;
-                            }
-                            child.setLocation((attributePanelEnd), child.getY());
-                        }
-                    }
-                }
-
-            }
-        });
-    }
-}
diff --git a/src/org/broad/igv/ui/util/ApplicationStatusBar.java b/src/org/broad/igv/ui/util/ApplicationStatusBar.java
index 3dcd302..e21234b 100644
--- a/src/org/broad/igv/ui/util/ApplicationStatusBar.java
+++ b/src/org/broad/igv/ui/util/ApplicationStatusBar.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -27,7 +27,6 @@ import com.jidesoft.status.MemoryStatusBarItem;
 import com.jidesoft.status.StatusBar;
 import com.jidesoft.swing.JideBoxLayout;
 import org.apache.log4j.Logger;
-import org.broad.igv.ui.GuiUtilities;
 
 import javax.swing.*;
 import java.awt.*;
@@ -63,6 +62,7 @@ public class ApplicationStatusBar extends StatusBar {
         add(messageBox2, JideBoxLayout.VARY);
 
         memoryBox = new MemoryStatusBarItem();
+        
         add(memoryBox, JideBoxLayout.FLEXIBLE);
     }
 
@@ -72,7 +72,7 @@ public class ApplicationStatusBar extends StatusBar {
      * @param message
      */
     public void setMessage(final String message) {
-        GuiUtilities.invokeOnEventThread(new Runnable() {
+        UIUtilities.invokeOnEventThread(new Runnable() {
 
             public void run() {
                 messageBox.setText(message);
diff --git a/src/org/broad/igv/ui/util/CheckList.java b/src/org/broad/igv/ui/util/CheckList.java
index f1f3bc7..5e66986 100644
--- a/src/org/broad/igv/ui/util/CheckList.java
+++ b/src/org/broad/igv/ui/util/CheckList.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -27,6 +27,8 @@ import org.broad.igv.util.Utilities;
 
 import javax.swing.*;
 import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
 import java.util.*;
 import java.util.List;
 
@@ -41,6 +43,7 @@ public class CheckList extends JPanel {
     private boolean defaultState = true;
     private JLabel header = null;
     private JPanel itemPanel = new JPanel();
+    private GridLayout layout;
 
     // Backup of state
     private Component[] componentState = new Component[0];
@@ -53,7 +56,8 @@ public class CheckList extends JPanel {
 
         setLayout(new BorderLayout());
         this.defaultState = defaultState;
-        itemPanel.setLayout(new GridLayout(0, 1));
+        layout = new GridLayout(0, 1);
+        itemPanel.setLayout(layout);
 
         if (headerText != null) {
             header = new JLabel(headerText);
@@ -62,21 +66,56 @@ public class CheckList extends JPanel {
         } else {
             add(itemPanel);
         }
+
+        JPanel checkClearPanel = new JPanel();
+        checkClearPanel.setLayout(new FlowLayout());
+
+        JButton checkAllButton = new JButton("Select All");
+        checkAllButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent actionEvent) {
+                setStateForAll(true);
+            }
+        });
+
+        JButton clearAllButton = new JButton("Clear All");
+        clearAllButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent actionEvent) {
+                setStateForAll(false);
+            }
+        });
+
+
+        checkClearPanel.add(checkAllButton);
+        checkClearPanel.add(clearAllButton);
+        add(checkClearPanel, BorderLayout.SOUTH);
     }
 
-    public void addItems(List<String> itemStrings) {
 
-        for (String itemText : itemStrings) {
-            addItem(itemText, defaultState);
+    private void setStateForAll(boolean selected) {
+        for(Component c : itemPanel.getComponents()) {
+            if(c instanceof JCheckBox) {
+                ((JCheckBox) c).setSelected(selected);
+            }
         }
         isDirty = true;
     }
 
+    public void addItems(List<String> itemStrings) {
+
+        addItems(itemStrings, defaultState);
+    }
+
     public void addItems(List<String> itemStrings, boolean isChecked) {
 
+        // Layout so that we have a maximum of 20 items per column
+        int nColumns = (itemStrings.size() / 20) + 1;
+        layout.setColumns(nColumns);
+
         for (String itemText : itemStrings) {
             addItem(itemText, isChecked);
         }
+        JButton checkAllButton = new JButton("Check All");
+        itemPanel.add(checkAllButton);
         isDirty = true;
     }
 
@@ -112,7 +151,7 @@ public class CheckList extends JPanel {
      */
     public void update() {
 
-        // Can't update while in the middle of editing
+        // Can't restorePersistentState while in the middle of editing
         if (isEditing()) {
             return;
         }
@@ -122,7 +161,7 @@ public class CheckList extends JPanel {
         componentState = new Component[length];
         int i = 0;
 
-        for (Component component : components) {
+        for (Component component : itemPanel.getComponents()) {
 
             boolean isChecked = ((JCheckBox) component).isSelected();
             String text = ((JCheckBox) component).getText();
@@ -144,7 +183,7 @@ public class CheckList extends JPanel {
 
     public HashSet<String> getSelectedItems() {
 
-        // If there has been a modification we need to update first
+        // If there has been a modification we need to restorePersistentState first
         if (isDirty) {
             update();
         }
@@ -170,7 +209,7 @@ public class CheckList extends JPanel {
 
     public HashSet<String> getUnselectedItems() {
 
-        // If there has been a modification we need to update first
+        // If there has been a modification we need to restorePersistentState first
         if (isDirty) {
             update();
         }
diff --git a/src/org/broad/igv/ui/util/CheckListDialog.java b/src/org/broad/igv/ui/util/CheckListDialog.java
new file mode 100644
index 0000000..88126f9
--- /dev/null
+++ b/src/org/broad/igv/ui/util/CheckListDialog.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * Created by JFormDesigner on Thu Aug 26 21:15:02 EDT 2010
+ */
+
+package org.broad.igv.ui.util;
+
+import org.broad.igv.util.Utilities;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.util.List;
+import javax.swing.*;
+import javax.swing.border.*;
+
+/**
+ * @author Jim Robinson
+ */
+public class CheckListDialog extends JDialog {
+
+    private boolean canceled = true;
+    private Set<String> selections;
+    private Set<String> nonSelections;
+    private List<JCheckBox> checkBoxes = new ArrayList();
+
+    public CheckListDialog(Frame owner, java.util.List<String> choices, Collection<String> selections, boolean selectionState) {
+        super(owner);
+        initComponents();
+        initCheckboxes(choices, selections, selectionState);
+    }
+
+    public CheckListDialog(Dialog owner) {
+        super(owner);
+        initComponents();
+    }
+
+    private void initCheckboxes(java.util.List<String> tmp, Collection<String> selections, boolean selectionState) {
+
+        // Copy list before sorting
+        java.util.List<String> choices = new ArrayList(tmp);
+        Collections.sort(choices, Utilities.getNumericStringComparator());
+
+        boolean allSelected = true;
+        for (String s : choices) {
+            JCheckBox cb = new JCheckBox(s);
+            if (selections == null) {
+                cb.setSelected(!selectionState);
+                allSelected = !selectionState;
+            } else {
+                if ((selectionState == true && selections.contains(s)) ||
+                        (selectionState == false && !selections.contains(s))) {
+                    cb.setSelected(true);
+                } else {
+                    allSelected = false;
+                    cb.setSelected(false);
+                }
+
+            }
+            checkboxPane.add(cb);
+            checkBoxes.add(cb);
+        }
+        allCB.setSelected(allSelected);
+        getContentPane().validate();
+    }
+
+
+    private void okButtonActionPerformed(ActionEvent e) {
+
+        selections = new HashSet();
+        nonSelections = new HashSet();
+        for (JCheckBox cb : checkBoxes) {
+            if (cb.isSelected()) {
+                selections.add(cb.getText());
+            } else {
+                nonSelections.add(cb.getText());
+            }
+        }
+        canceled = false;
+        setVisible(false);
+    }
+
+    private void cancelButtonActionPerformed(ActionEvent e) {
+        canceled = true;
+        setVisible(false);
+    }
+
+    private void allCBActionPerformed(ActionEvent e) {
+        for(JCheckBox cb : checkBoxes) {
+            cb.setSelected(allCB.isSelected());
+        }
+    }
+
+    private void initComponents() {
+        // JFormDesigner - Component initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
+        // Generated using JFormDesigner non-commercial license
+        dialogPane = new JPanel();
+        contentPanel = new JPanel();
+        scrollPane1 = new JScrollPane();
+        checkboxPane = new JPanel();
+        allCB = new JCheckBox();
+        buttonBar = new JPanel();
+        okButton = new JButton();
+        cancelButton = new JButton();
+
+        //======== this ========
+        setResizable(false);
+        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+        setModal(true);
+        Container contentPane = getContentPane();
+        contentPane.setLayout(null);
+
+        //======== dialogPane ========
+        {
+            dialogPane.setBorder(new EmptyBorder(12, 12, 12, 12));
+            dialogPane.setLayout(new BorderLayout());
+
+            //======== contentPanel ========
+            {
+                contentPanel.setLayout(new BorderLayout());
+
+                //======== scrollPane1 ========
+                {
+
+                    //======== checkboxPane ========
+                    {
+                        checkboxPane.setLayout(new BoxLayout(checkboxPane, BoxLayout.Y_AXIS));
+
+                        //---- allCB ----
+                        allCB.setText("All");
+                        allCB.addActionListener(new ActionListener() {
+                            public void actionPerformed(ActionEvent e) {
+                                allCBActionPerformed(e);
+                            }
+                        });
+                        checkboxPane.add(allCB);
+                    }
+                    scrollPane1.setViewportView(checkboxPane);
+                }
+                contentPanel.add(scrollPane1, BorderLayout.CENTER);
+            }
+            dialogPane.add(contentPanel, BorderLayout.CENTER);
+
+            //======== buttonBar ========
+            {
+                buttonBar.setBorder(new EmptyBorder(12, 0, 0, 0));
+                buttonBar.setLayout(new GridBagLayout());
+                ((GridBagLayout)buttonBar.getLayout()).columnWidths = new int[] {0, 85, 80};
+                ((GridBagLayout)buttonBar.getLayout()).columnWeights = new double[] {1.0, 0.0, 0.0};
+
+                //---- okButton ----
+                okButton.setText("OK");
+                okButton.addActionListener(new ActionListener() {
+                    public void actionPerformed(ActionEvent e) {
+                        okButtonActionPerformed(e);
+                    }
+                });
+                buttonBar.add(okButton, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.BOTH,
+                    new Insets(0, 0, 0, 5), 0, 0));
+
+                //---- cancelButton ----
+                cancelButton.setText("Cancel");
+                cancelButton.addActionListener(new ActionListener() {
+                    public void actionPerformed(ActionEvent e) {
+                        cancelButtonActionPerformed(e);
+                    }
+                });
+                buttonBar.add(cancelButton, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0,
+                    GridBagConstraints.CENTER, GridBagConstraints.BOTH,
+                    new Insets(0, 0, 0, 0), 0, 0));
+            }
+            dialogPane.add(buttonBar, BorderLayout.SOUTH);
+        }
+        contentPane.add(dialogPane);
+        dialogPane.setBounds(0, 0, 468, 551);
+
+        { // compute preferred size
+            Dimension preferredSize = new Dimension();
+            for(int i = 0; i < contentPane.getComponentCount(); i++) {
+                Rectangle bounds = contentPane.getComponent(i).getBounds();
+                preferredSize.width = Math.max(bounds.x + bounds.width, preferredSize.width);
+                preferredSize.height = Math.max(bounds.y + bounds.height, preferredSize.height);
+            }
+            Insets insets = contentPane.getInsets();
+            preferredSize.width += insets.right;
+            preferredSize.height += insets.bottom;
+            contentPane.setMinimumSize(preferredSize);
+            contentPane.setPreferredSize(preferredSize);
+        }
+        setSize(470, 575);
+        setLocationRelativeTo(getOwner());
+        // JFormDesigner - End of component initialization  //GEN-END:initComponents
+    }
+
+    // JFormDesigner - Variables declaration - DO NOT MODIFY  //GEN-BEGIN:variables
+    // Generated using JFormDesigner non-commercial license
+    private JPanel dialogPane;
+    private JPanel contentPanel;
+    private JScrollPane scrollPane1;
+    private JPanel checkboxPane;
+    private JCheckBox allCB;
+    private JPanel buttonBar;
+    private JButton okButton;
+    private JButton cancelButton;
+    // JFormDesigner - End of variables declaration  //GEN-END:variables
+
+    public boolean isCanceled() {
+        return canceled;
+    }
+
+    public Set<String> getSelections() {
+        return selections;
+    }
+
+    public Set<String> getNonSelections() {
+        return nonSelections;
+    }
+}
diff --git a/src/org/broad/igv/ui/util/CheckListDialog.jfd b/src/org/broad/igv/ui/util/CheckListDialog.jfd
new file mode 100644
index 0000000..9a66442
--- /dev/null
+++ b/src/org/broad/igv/ui/util/CheckListDialog.jfd
@@ -0,0 +1,267 @@
+<?xml version="1.0" encoding="UTF-8"?> 
+<java version="1.6.0_17" class="java.beans.XMLDecoder"> 
+ <object class="com.jformdesigner.model.FormModel"> 
+  <void property="contentType"> 
+   <string>form/swing</string> 
+  </void> 
+  <void property="root"> 
+   <object class="com.jformdesigner.model.FormRoot"> 
+    <void method="add"> 
+     <object class="com.jformdesigner.model.FormWindow"> 
+      <string>javax.swing.JDialog</string> 
+      <object class="com.jformdesigner.model.FormLayoutManager"> 
+       <class>com.jformdesigner.runtime.NullLayout</class> 
+      </object> 
+      <void method="setProperty"> 
+       <string>$sizePolicy</string> 
+       <int>1</int> 
+      </void> 
+      <void method="setProperty"> 
+       <string>resizable</string> 
+       <boolean>false</boolean> 
+      </void> 
+      <void method="setProperty"> 
+       <string>defaultCloseOperation</string> 
+       <int>2</int> 
+      </void> 
+      <void method="setProperty"> 
+       <string>modal</string> 
+       <boolean>true</boolean> 
+      </void> 
+      <void method="add"> 
+       <object class="com.jformdesigner.model.FormContainer"> 
+        <string>javax.swing.JPanel</string> 
+        <object class="com.jformdesigner.model.FormLayoutManager"> 
+         <class>java.awt.BorderLayout</class> 
+        </object> 
+        <void method="setProperty"> 
+         <string>border</string> 
+         <object class="javax.swing.border.EmptyBorder"> 
+          <object class="java.awt.Insets"> 
+           <int>12</int> 
+           <int>12</int> 
+           <int>12</int> 
+           <int>12</int> 
+          </object> 
+         </object> 
+        </void> 
+        <void property="name"> 
+         <string>dialogPane</string> 
+        </void> 
+        <void method="add"> 
+         <object class="com.jformdesigner.model.FormContainer"> 
+          <string>javax.swing.JPanel</string> 
+          <object class="com.jformdesigner.model.FormLayoutManager"> 
+           <class>java.awt.BorderLayout</class> 
+          </object> 
+          <void property="name"> 
+           <string>contentPanel</string> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormContainer"> 
+            <string>javax.swing.JScrollPane</string> 
+            <object class="com.jformdesigner.model.FormLayoutManager"> 
+             <class>javax.swing.JScrollPane</class> 
+            </object> 
+            <void property="name"> 
+             <string>scrollPane1</string> 
+            </void> 
+            <void method="add"> 
+             <object class="com.jformdesigner.model.FormContainer"> 
+              <string>javax.swing.JPanel</string> 
+              <object class="com.jformdesigner.model.FormLayoutManager"> 
+               <class>javax.swing.BoxLayout</class> 
+               <void method="setProperty"> 
+                <string>axis</string> 
+                <int>1</int> 
+               </void> 
+              </object> 
+              <void property="name"> 
+               <string>checkboxPane</string> 
+              </void> 
+              <void method="add"> 
+               <object class="com.jformdesigner.model.FormComponent"> 
+                <string>javax.swing.JCheckBox</string> 
+                <void method="setProperty"> 
+                 <string>text</string> 
+                 <string>All</string> 
+                </void> 
+                <void property="name"> 
+                 <string>allCB</string> 
+                </void> 
+                <void method="addEvent"> 
+                 <object class="com.jformdesigner.model.FormEvent"> 
+                  <string>java.awt.event.ActionListener</string> 
+                  <string>actionPerformed</string> 
+                  <string>allCBActionPerformed</string> 
+                  <boolean>true</boolean> 
+                 </object> 
+                </void> 
+               </object> 
+              </void> 
+             </object> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>java.lang.String</class> 
+            <void method="setProperty"> 
+             <string>value</string> 
+             <string>Center</string> 
+            </void> 
+           </object> 
+          </void> 
+         </object> 
+         <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+          <class>java.lang.String</class> 
+          <void method="setProperty"> 
+           <string>value</string> 
+           <string>Center</string> 
+          </void> 
+         </object> 
+        </void> 
+        <void method="add"> 
+         <object class="com.jformdesigner.model.FormContainer"> 
+          <string>javax.swing.JPanel</string> 
+          <object class="com.jformdesigner.model.FormLayoutManager"> 
+           <class>java.awt.GridBagLayout</class> 
+           <void method="setProperty"> 
+            <string>$columnSpecs</string> 
+            <string>0:1.0, 80, 80</string> 
+           </void> 
+           <void method="setProperty"> 
+            <string>$rowSpecs</string> 
+            <string>0</string> 
+           </void> 
+           <void method="setProperty"> 
+            <string>$hGap</string> 
+            <int>5</int> 
+           </void> 
+           <void method="setProperty"> 
+            <string>$vGap</string> 
+            <int>5</int> 
+           </void> 
+          </object> 
+          <void method="setProperty"> 
+           <string>border</string> 
+           <object class="javax.swing.border.EmptyBorder"> 
+            <object class="java.awt.Insets"> 
+             <int>12</int> 
+             <int>0</int> 
+             <int>0</int> 
+             <int>0</int> 
+            </object> 
+           </object> 
+          </void> 
+          <void property="name"> 
+           <string>buttonBar</string> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormComponent"> 
+            <string>javax.swing.JButton</string> 
+            <void method="setProperty"> 
+             <string>text</string> 
+             <string>OK</string> 
+            </void> 
+            <void property="name"> 
+             <string>okButton</string> 
+            </void> 
+            <void method="addEvent"> 
+             <object class="com.jformdesigner.model.FormEvent"> 
+              <string>java.awt.event.ActionListener</string> 
+              <string>actionPerformed</string> 
+              <string>okButtonActionPerformed</string> 
+              <boolean>true</boolean> 
+             </object> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.GridBagConstraintsEx</class> 
+            <void method="setProperty"> 
+             <string>gridx</string> 
+             <int>1</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>gridy</string> 
+             <int>0</int> 
+            </void> 
+           </object> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormComponent"> 
+            <string>javax.swing.JButton</string> 
+            <void method="setProperty"> 
+             <string>text</string> 
+             <string>Cancel</string> 
+            </void> 
+            <void property="name"> 
+             <string>cancelButton</string> 
+            </void> 
+            <void method="addEvent"> 
+             <object class="com.jformdesigner.model.FormEvent"> 
+              <string>java.awt.event.ActionListener</string> 
+              <string>actionPerformed</string> 
+              <string>cancelButtonActionPerformed</string> 
+              <boolean>true</boolean> 
+             </object> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.GridBagConstraintsEx</class> 
+            <void method="setProperty"> 
+             <string>gridx</string> 
+             <int>2</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>gridy</string> 
+             <int>0</int> 
+            </void> 
+           </object> 
+          </void> 
+         </object> 
+         <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+          <class>java.lang.String</class> 
+          <void method="setProperty"> 
+           <string>value</string> 
+           <string>South</string> 
+          </void> 
+         </object> 
+        </void> 
+       </object> 
+       <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+        <class>com.jformdesigner.runtime.NullConstraints</class> 
+        <void method="setProperty"> 
+         <string>width</string> 
+         <int>468</int> 
+        </void> 
+        <void method="setProperty"> 
+         <string>height</string> 
+         <int>551</int> 
+        </void> 
+       </object> 
+      </void> 
+      <void property="name"> 
+       <string>this</string> 
+      </void> 
+     </object> 
+     <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+      <null/> 
+      <void method="setProperty"> 
+       <string>location</string> 
+       <object class="java.awt.Point"> 
+        <int>0</int> 
+        <int>0</int> 
+       </object> 
+      </void> 
+      <void method="setProperty"> 
+       <string>size</string> 
+       <object class="java.awt.Dimension"> 
+        <int>470</int> 
+        <int>575</int> 
+       </object> 
+      </void> 
+     </object> 
+    </void> 
+   </object> 
+  </void> 
+ </object> 
+</java> 
diff --git a/src/org/broad/igv/ui/util/ColorTable.java b/src/org/broad/igv/ui/util/ColorTable.java
new file mode 100644
index 0000000..8284654
--- /dev/null
+++ b/src/org/broad/igv/ui/util/ColorTable.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.ui.util;
+
+import org.broad.igv.util.ColorUtilities;
+
+import java.awt.*;
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * User: jrobinso
+ * Date: Feb 26, 2010
+ */
+public class ColorTable {
+
+    Map<String, Color> colors;
+
+    public ColorTable() {
+        colors = new Hashtable();
+    }
+
+    public void put(String key, Color c) {
+        colors.put(key, c);
+    }
+
+    public Color get(String key) {
+        Color c = colors.get(key);
+        if (c == null) {
+            c = ColorUtilities.randomColor(colors.size());
+            colors.put(key, c);
+        }
+        return c;
+    }
+
+    public Collection<String> getKeys() {
+        return colors.keySet();
+    }
+
+    public Set<Map.Entry<String, Color>> entrySet() {
+        return colors.entrySet();
+    }
+}
diff --git a/src/org/broad/igv/ui/util/ConfirmDialog.java b/src/org/broad/igv/ui/util/ConfirmDialog.java
new file mode 100644
index 0000000..6f42ec3
--- /dev/null
+++ b/src/org/broad/igv/ui/util/ConfirmDialog.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * Created by JFormDesigner on Sun Aug 15 22:36:06 EDT 2010
+ */
+
+package org.broad.igv.ui.util;
+
+import java.awt.event.*;
+
+import org.broad.igv.PreferenceManager;
+import org.broad.igv.ui.IGVMainFrame;
+
+import java.awt.*;
+import javax.swing.*;
+import javax.swing.border.*;
+
+/**
+ * A confirm dialog with a "do not show again" checkbox.  For release 1.5rc2 this class is more or less hardocded
+ * for the "check size" function of TrackLoader.
+ *
+ * @author Jim Robinson
+ */
+public class ConfirmDialog extends JDialog {
+
+    String key;
+    boolean okPressed = false;
+
+    private ConfirmDialog(Frame owner, String message, String key) {
+        super(owner);
+        this.setModal(true);
+        initComponents();
+        label.setText("<html>" + message + "</html>");
+        okButton.setText("Continue");
+        this.key = key;
+        getRootPane().setDefaultButton(cancelButton);
+    }
+
+
+    private void okButtonActionPerformed(ActionEvent e) {
+        if (doNotShowCheckbox.isSelected()) {
+            PreferenceManager.getInstance().put(key, "false");
+        }
+        okPressed = true;
+        setVisible(false);
+    }
+
+    private void cancelButtonActionPerformed(ActionEvent e) {
+        if (doNotShowCheckbox.isSelected()) {
+            PreferenceManager.getInstance().put(key, "false");
+        }
+        okPressed = false;
+        setVisible(false);
+    }
+
+
+    private void initComponents() {
+        // JFormDesigner - Component initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
+        // Generated using JFormDesigner non-commercial license
+        dialogPane = new JPanel();
+        contentPanel = new JPanel();
+        label = new JLabel();
+        buttonBar = new JPanel();
+        doNotShowCheckbox = new JCheckBox();
+        cancelButton = new JButton();
+        okButton = new JButton();
+
+        //======== this ========
+        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+        setResizable(false);
+        Container contentPane = getContentPane();
+        contentPane.setLayout(new BorderLayout());
+
+        //======== dialogPane ========
+        {
+            dialogPane.setBorder(new EmptyBorder(12, 12, 12, 12));
+            dialogPane.setLayout(new BorderLayout());
+
+            //======== contentPanel ========
+            {
+                contentPanel.setLayout(null);
+
+                //---- label ----
+                label.setText("text");
+                contentPanel.add(label);
+                label.setBounds(10, 0, 565, 195);
+
+                { // compute preferred size
+                    Dimension preferredSize = new Dimension();
+                    for (int i = 0; i < contentPanel.getComponentCount(); i++) {
+                        Rectangle bounds = contentPanel.getComponent(i).getBounds();
+                        preferredSize.width = Math.max(bounds.x + bounds.width, preferredSize.width);
+                        preferredSize.height = Math.max(bounds.y + bounds.height, preferredSize.height);
+                    }
+                    Insets insets = contentPanel.getInsets();
+                    preferredSize.width += insets.right;
+                    preferredSize.height += insets.bottom;
+                    contentPanel.setMinimumSize(preferredSize);
+                    contentPanel.setPreferredSize(preferredSize);
+                }
+            }
+            dialogPane.add(contentPanel, BorderLayout.CENTER);
+
+            //======== buttonBar ========
+            {
+                buttonBar.setBorder(new EmptyBorder(12, 0, 0, 0));
+                buttonBar.setLayout(new GridBagLayout());
+                ((GridBagLayout) buttonBar.getLayout()).columnWidths = new int[]{0, 0, 80};
+                ((GridBagLayout) buttonBar.getLayout()).columnWeights = new double[]{1.0, 0.0, 0.0};
+
+                //---- doNotShowCheckbox ----
+                doNotShowCheckbox.setText("Do not show this message again.");
+                buttonBar.add(doNotShowCheckbox, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
+                        GridBagConstraints.CENTER, GridBagConstraints.BOTH,
+                        new Insets(0, 0, 5, 5), 0, 0));
+
+                //---- cancelButton ----
+                cancelButton.setText("Cancel");
+                cancelButton.addActionListener(new ActionListener() {
+                    public void actionPerformed(ActionEvent e) {
+                        cancelButtonActionPerformed(e);
+                    }
+                });
+                buttonBar.add(cancelButton, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0,
+                        GridBagConstraints.CENTER, GridBagConstraints.BOTH,
+                        new Insets(0, 0, 5, 5), 0, 0));
+
+                //---- okButton ----
+                okButton.setText("OK");
+                okButton.addActionListener(new ActionListener() {
+                    public void actionPerformed(ActionEvent e) {
+                        okButtonActionPerformed(e);
+                    }
+                });
+                buttonBar.add(okButton, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0,
+                        GridBagConstraints.CENTER, GridBagConstraints.BOTH,
+                        new Insets(0, 0, 5, 0), 0, 0));
+            }
+            dialogPane.add(buttonBar, BorderLayout.SOUTH);
+        }
+        contentPane.add(dialogPane, BorderLayout.CENTER);
+        setSize(600, 300);
+        setLocationRelativeTo(getOwner());
+        // JFormDesigner - End of component initialization  //GEN-END:initComponents
+    }
+
+    public static void optionallyShowInfoDialog(String message, String key) {
+
+        boolean show = PreferenceManager.getInstance().getBooleanPreference(key, true);
+        if (show) {
+            ConfirmDialog dlg = new ConfirmDialog(IGVMainFrame.getInstance(), message, key);
+            dlg.okButton.setText("OK");
+            dlg.cancelButton.setVisible(false);
+            dlg.setVisible(true);
+
+        }
+    }
+
+    public static boolean optionallyShowInfoDialog(String message, String key, boolean defaultValue) {
+
+        boolean show = PreferenceManager.getInstance().getBooleanPreference(key, true);
+        if (show) {
+            ConfirmDialog dlg = new ConfirmDialog(IGVMainFrame.getInstance(), message, key);
+
+            dlg.setVisible(true);
+            return dlg.okPressed;
+
+        }
+        return defaultValue;
+    }
+
+    // JFormDesigner - Variables declaration - DO NOT MODIFY  //GEN-BEGIN:variables
+    // Generated using JFormDesigner non-commercial license
+    private JPanel dialogPane;
+    private JPanel contentPanel;
+    private JLabel label;
+    private JPanel buttonBar;
+    private JCheckBox doNotShowCheckbox;
+    private JButton cancelButton;
+    private JButton okButton;
+    // JFormDesigner - End of variables declaration  //GEN-END:variables
+
+
+    public static void main(String[] args) {
+
+        ConfirmDialog.optionallyShowInfoDialog("asdfasd sfs sdfdsfds ddddf ef  feeeeeeeeef dfd sdfd  sdfd " +
+                "asdfasd sfs sdfdsfds ddddf ef  feeeeeeeeef dfd sdfd  sdfd " + "asdfasd sfs sdfdsfds ddddf ef  feeeeeeeeef dfd sdfd  sdfd ",
+                "focccsfcccco", true);
+
+    }
+}
diff --git a/src/org/broad/igv/ui/util/ConfirmDialog.jfd b/src/org/broad/igv/ui/util/ConfirmDialog.jfd
new file mode 100644
index 0000000..000f8aa
--- /dev/null
+++ b/src/org/broad/igv/ui/util/ConfirmDialog.jfd
@@ -0,0 +1,248 @@
+<?xml version="1.0" encoding="UTF-8"?> 
+<java version="1.6.0_17" class="java.beans.XMLDecoder"> 
+ <object class="com.jformdesigner.model.FormModel"> 
+  <void property="contentType"> 
+   <string>form/swing</string> 
+  </void> 
+  <void property="root"> 
+   <object class="com.jformdesigner.model.FormRoot"> 
+    <void method="add"> 
+     <object class="com.jformdesigner.model.FormWindow"> 
+      <string>javax.swing.JDialog</string> 
+      <object class="com.jformdesigner.model.FormLayoutManager"> 
+       <class>java.awt.BorderLayout</class> 
+      </object> 
+      <void method="setProperty"> 
+       <string>defaultCloseOperation</string> 
+       <int>2</int> 
+      </void> 
+      <void method="setProperty"> 
+       <string>$sizePolicy</string> 
+       <int>1</int> 
+      </void> 
+      <void method="setProperty"> 
+       <string>resizable</string> 
+       <boolean>false</boolean> 
+      </void> 
+      <void method="add"> 
+       <object class="com.jformdesigner.model.FormContainer"> 
+        <string>javax.swing.JPanel</string> 
+        <object class="com.jformdesigner.model.FormLayoutManager"> 
+         <class>java.awt.BorderLayout</class> 
+        </object> 
+        <void method="setProperty"> 
+         <string>border</string> 
+         <object class="javax.swing.border.EmptyBorder"> 
+          <object class="java.awt.Insets"> 
+           <int>12</int> 
+           <int>12</int> 
+           <int>12</int> 
+           <int>12</int> 
+          </object> 
+         </object> 
+        </void> 
+        <void property="name"> 
+         <string>dialogPane</string> 
+        </void> 
+        <void method="add"> 
+         <object class="com.jformdesigner.model.FormContainer"> 
+          <string>javax.swing.JPanel</string> 
+          <object class="com.jformdesigner.model.FormLayoutManager"> 
+           <class>com.jformdesigner.runtime.NullLayout</class> 
+          </object> 
+          <void property="name"> 
+           <string>contentPanel</string> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormComponent"> 
+            <string>javax.swing.JLabel</string> 
+            <void method="setProperty"> 
+             <string>text</string> 
+             <string>text</string> 
+            </void> 
+            <void property="name"> 
+             <string>label</string> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.NullConstraints</class> 
+            <void method="setProperty"> 
+             <string>x</string> 
+             <int>10</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>y</string> 
+             <int>0</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>width</string> 
+             <int>565</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>height</string> 
+             <int>195</int> 
+            </void> 
+           </object> 
+          </void> 
+         </object> 
+         <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+          <class>java.lang.String</class> 
+          <void method="setProperty"> 
+           <string>value</string> 
+           <string>Center</string> 
+          </void> 
+         </object> 
+        </void> 
+        <void method="add"> 
+         <object class="com.jformdesigner.model.FormContainer"> 
+          <string>javax.swing.JPanel</string> 
+          <object class="com.jformdesigner.model.FormLayoutManager"> 
+           <class>java.awt.GridBagLayout</class> 
+           <void method="setProperty"> 
+            <string>$columnSpecs</string> 
+            <string>0:1.0, 0, 80</string> 
+           </void> 
+           <void method="setProperty"> 
+            <string>$rowSpecs</string> 
+            <string>0, 0</string> 
+           </void> 
+           <void method="setProperty"> 
+            <string>$hGap</string> 
+            <int>5</int> 
+           </void> 
+           <void method="setProperty"> 
+            <string>$vGap</string> 
+            <int>5</int> 
+           </void> 
+          </object> 
+          <void method="setProperty"> 
+           <string>border</string> 
+           <object class="javax.swing.border.EmptyBorder"> 
+            <object class="java.awt.Insets"> 
+             <int>12</int> 
+             <int>0</int> 
+             <int>0</int> 
+             <int>0</int> 
+            </object> 
+           </object> 
+          </void> 
+          <void property="name"> 
+           <string>buttonBar</string> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormComponent"> 
+            <string>javax.swing.JCheckBox</string> 
+            <void method="setProperty"> 
+             <string>text</string> 
+             <string>Do not show this message again.</string> 
+            </void> 
+            <void property="name"> 
+             <string>doNotShowCheckbox</string> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.GridBagConstraintsEx</class> 
+           </object> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormComponent"> 
+            <string>javax.swing.JButton</string> 
+            <void method="setProperty"> 
+             <string>text</string> 
+             <string>Cancel</string> 
+            </void> 
+            <void property="name"> 
+             <string>cancelButton</string> 
+            </void> 
+            <void method="addEvent"> 
+             <object class="com.jformdesigner.model.FormEvent"> 
+              <string>java.awt.event.ActionListener</string> 
+              <string>actionPerformed</string> 
+              <string>cancelButtonActionPerformed</string> 
+              <boolean>true</boolean> 
+             </object> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.GridBagConstraintsEx</class> 
+            <void method="setProperty"> 
+             <string>gridx</string> 
+             <int>1</int> 
+            </void> 
+           </object> 
+          </void> 
+          <void method="add"> 
+           <object class="com.jformdesigner.model.FormComponent"> 
+            <string>javax.swing.JButton</string> 
+            <void method="setProperty"> 
+             <string>text</string> 
+             <string>OK</string> 
+            </void> 
+            <void property="name"> 
+             <string>okButton</string> 
+            </void> 
+            <void method="addEvent"> 
+             <object class="com.jformdesigner.model.FormEvent"> 
+              <string>java.awt.event.ActionListener</string> 
+              <string>actionPerformed</string> 
+              <string>okButtonActionPerformed</string> 
+              <boolean>true</boolean> 
+             </object> 
+            </void> 
+           </object> 
+           <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+            <class>com.jformdesigner.runtime.GridBagConstraintsEx</class> 
+            <void method="setProperty"> 
+             <string>gridx</string> 
+             <int>2</int> 
+            </void> 
+            <void method="setProperty"> 
+             <string>gridy</string> 
+             <int>0</int> 
+            </void> 
+           </object> 
+          </void> 
+         </object> 
+         <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+          <class>java.lang.String</class> 
+          <void method="setProperty"> 
+           <string>value</string> 
+           <string>South</string> 
+          </void> 
+         </object> 
+        </void> 
+       </object> 
+       <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+        <class>java.lang.String</class> 
+        <void method="setProperty"> 
+         <string>value</string> 
+         <string>Center</string> 
+        </void> 
+       </object> 
+      </void> 
+      <void property="name"> 
+       <string>this</string> 
+      </void> 
+     </object> 
+     <object class="com.jformdesigner.model.FormLayoutConstraints"> 
+      <null/> 
+      <void method="setProperty"> 
+       <string>location</string> 
+       <object class="java.awt.Point"> 
+        <int>0</int> 
+        <int>0</int> 
+       </object> 
+      </void> 
+      <void method="setProperty"> 
+       <string>size</string> 
+       <object class="java.awt.Dimension"> 
+        <int>600</int> 
+        <int>300</int> 
+       </object> 
+      </void> 
+     </object> 
+    </void> 
+   </object> 
+  </void> 
+ </object> 
+</java> 
diff --git a/src/org/broad/igv/ui/util/FileChooser.java b/src/org/broad/igv/ui/util/FileChooser.java
index eb2d584..558d080 100644
--- a/src/org/broad/igv/ui/util/FileChooser.java
+++ b/src/org/broad/igv/ui/util/FileChooser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/util/FileChooserDialog.java b/src/org/broad/igv/ui/util/FileChooserDialog.java
index b68b8d3..323ed19 100644
--- a/src/org/broad/igv/ui/util/FileChooserDialog.java
+++ b/src/org/broad/igv/ui/util/FileChooserDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,7 +23,7 @@
  */
 package org.broad.igv.ui.util;
 
-import org.broad.igv.IGVConstants;
+import org.broad.igv.ui.UIConstants;
 
 import javax.swing.*;
 import javax.swing.filechooser.FileFilter;
@@ -98,7 +98,7 @@ public class FileChooserDialog extends javax.swing.JDialog {
 
         // MAC has a bug in rescanCurrentDirectory() which is supposes to get 
         // fixed in Java version 6
-        if (!IGVConstants.IS_MAC) {
+        if (!UIConstants.IS_MAC) {
             fileChooser.rescanCurrentDirectory();
         }
         super.setVisible(value);
@@ -140,25 +140,6 @@ public class FileChooserDialog extends javax.swing.JDialog {
         return isCanceled;
     }
 
-    public void setDisposeOnOk(boolean isDisposableOnOk) {
-        this.isDisposableOnOk = isDisposableOnOk;
-    }
-
-    public void setDisposeOnCancel(boolean isDisposableOnCancel) {
-        this.isDisposableOnCancel = isDisposableOnCancel;
-    }
-
-    public void addChoosableFileFilter(FileFilter filter) {
-        fileChooser.addChoosableFileFilter(filter);
-    }
-
-    public FileFilter[] getChoosableFileFilters() {
-        return fileChooser.getChoosableFileFilters();
-    }
-
-    public void removeChoosableFileFilter(FileFilter filter) {
-        fileChooser.removeChoosableFileFilter(filter);
-    }
 
     public void setMultiSelectionEnabled(boolean isEnabled) {
         fileChooser.setMultiSelectionEnabled(true);
@@ -174,25 +155,11 @@ public class FileChooserDialog extends javax.swing.JDialog {
         }
     }
 
-    /**
-     * Determines if a call to setCurrentFile will automatically remember
-     * the file passed as the last file used. This will not remember
-     * choices made via the UI - that must be set manually.
-     *
-     * @param value false if it should not remember last file (default = true)
-     */
-    public void setAutoRememberLastFile(boolean value) {
-        autoRememberLastFile = value;
-    }
 
     public void setPreviousFile(File lastFile) {
         this.previousFile = lastFile;
     }
 
-    public File getPreviousFile() {
-        return previousFile;
-    }
-
     public void setCurrentDirectory(File file) {
 
         fileChooser.setCurrentDirectory(file);
@@ -204,37 +171,10 @@ public class FileChooserDialog extends javax.swing.JDialog {
         fileChooser.rescanCurrentDirectory();
     }
 
-    public File getCurrentDirectory() {
-        return fileChooser.getCurrentDirectory();
-    }
-
-    /**
-     * Determines if a call to setCurrentDirectory will automatically remember
-     * the file passed as the last directory used. This will not remember
-     * choices made via the UI - that must be set manually.
-     *
-     * @param value false if it should not remember last directory (default = true)
-     */
-    public void setAutoRememberLastDirectory(boolean value) {
-        autoRememberLastDirectory = value;
-    }
-
     public void setLastDirectory(File lastDirectory) {
         this.lastDirectory = lastDirectory;
     }
 
-    public File getLastDirectory() {
-
-        if (lastDirectory == null) {
-            lastDirectory = fileChooser.getCurrentDirectory();
-        }
-
-        return lastDirectory;
-    }
-
-    public void addFileChooserPropertyChangeListener(PropertyChangeListener listener) {
-        fileChooser.addPropertyChangeListener(listener);
-    }
 
     /**
      * This method is called from within the constructor to
diff --git a/src/org/broad/igv/ui/util/FilterComponent.java b/src/org/broad/igv/ui/util/FilterComponent.java
index 083e9e3..cda1355 100644
--- a/src/org/broad/igv/ui/util/FilterComponent.java
+++ b/src/org/broad/igv/ui/util/FilterComponent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -186,7 +186,7 @@ abstract public class FilterComponent extends javax.swing.JPanel {
 
         setBackground(new java.awt.Color(255, 255, 255));
         setMinimumSize(new java.awt.Dimension(530, 40));
-        setPreferredSize(new java.awt.Dimension(530, 40));
+        setPreferredSize(new java.awt.Dimension(700, 40));
         setRequestFocusEnabled(false);
         setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT, 2, 5));
 
@@ -213,7 +213,7 @@ abstract public class FilterComponent extends javax.swing.JPanel {
 
         comparisonOperatorComboBox.setActionCommand("comparisonOperatorComboBoxChanged");
         comparisonOperatorComboBox.setMinimumSize(new java.awt.Dimension(50, 27));
-        comparisonOperatorComboBox.setPreferredSize(new java.awt.Dimension(100, 27));
+        comparisonOperatorComboBox.setPreferredSize(new java.awt.Dimension(150, 27));
         comparisonOperatorComboBox.addActionListener(new java.awt.event.ActionListener() {
             public void actionPerformed(java.awt.event.ActionEvent evt) {
                 comparisonOperatorComboBoxActionPerformed(evt);
diff --git a/src/org/broad/igv/ui/util/FilterPane.java b/src/org/broad/igv/ui/util/FilterPane.java
index 6acaa8b..9175d2d 100644
--- a/src/org/broad/igv/ui/util/FilterPane.java
+++ b/src/org/broad/igv/ui/util/FilterPane.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -60,8 +60,7 @@ abstract public class FilterPane extends javax.swing.JPanel {
             Iterator iterator = filter.getFilterElements();
             while (iterator.hasNext()) {
                 FilterElement element = (FilterElement) iterator.next();
-                filterComponent =
-                        createFilterComponent(this, itemListLabel, itemList, element);
+                filterComponent = createFilterComponent(this, itemListLabel, itemList, element);
                 filterComponent.displayMoreButton(false);
                 add(filterComponent);
             }
@@ -86,13 +85,6 @@ abstract public class FilterPane extends javax.swing.JPanel {
             FilterPane filterPane, String itemListLabel, List<String> items,
             FilterElement element);
 
-    public void setFilterEnabled(boolean value) {
-        filter.setEnabled(value);
-    }
-
-    public boolean isFilterEnabled() {
-        return filter.isEnabled();
-    }
 
     public Filter getFilter() {
         return filter;
@@ -107,9 +99,6 @@ abstract public class FilterPane extends javax.swing.JPanel {
         itemList = new ArrayList<String>(items);
     }
 
-    public List<String> getItems() {
-        return itemList;
-    }
 
     /**
      * Add another filter
@@ -314,8 +303,8 @@ abstract public class FilterPane extends javax.swing.JPanel {
     private void initComponents() {
 
         setBackground(new java.awt.Color(255, 255, 255));
-        setMinimumSize(new java.awt.Dimension(510, 100));
-        setPreferredSize(new java.awt.Dimension(510, 100));
+        setMinimumSize(new java.awt.Dimension(500, 100));
+        setPreferredSize(new java.awt.Dimension(700, 100));
         setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT, 5, 0));
     }// </editor-fold>//GEN-END:initComponents
 
diff --git a/src/org/broad/igv/ui/util/GenericUtilities.java b/src/org/broad/igv/ui/util/GenericUtilities.java
index c175500..1ee2e83 100644
--- a/src/org/broad/igv/ui/util/GenericUtilities.java
+++ b/src/org/broad/igv/ui/util/GenericUtilities.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -25,9 +25,12 @@
  */
 package org.broad.igv.ui.util;
 
+import org.apache.batik.dom.GenericDOMImplementation;
+import org.apache.batik.svggen.SVGGraphics2D;
 import org.apache.log4j.Logger;
-import org.broad.igv.IGVConstants;
-import org.jibble.epsgraphics.EpsGraphics2D;
+import org.broad.igv.Globals;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
 
 import javax.imageio.ImageIO;
 import javax.swing.filechooser.FileFilter;
@@ -50,14 +53,13 @@ public class GenericUtilities {
 
     static {
 
-        SNAPSHOT_TYPE_TO_FILTER.put(SnapshotFileType.JPEG,
-                new SnapshotFileFilter(SnapshotFileType.JPEG));
+        SNAPSHOT_TYPE_TO_FILTER.put(SnapshotFileType.JPEG, new SnapshotFileFilter(SnapshotFileType.JPEG));
         //SNAPSHOT_TYPE_TO_FILTER.put(SnapshotFileType.PDF,
         //    new SnapshotFileFilter(SnapshotFileType.PDF));
-        SNAPSHOT_TYPE_TO_FILTER.put(SnapshotFileType.EPS,
-                new SnapshotFileFilter(SnapshotFileType.EPS));
-        //SNAPSHOT_TYPE_TO_FILTER.put(SnapshotFileType.SVG,
-        //    new SnapshotFileFilter(SnapshotFileType.SVG));
+        //SNAPSHOT_TYPE_TO_FILTER.put(SnapshotFileType.EPS,
+        //        new SnapshotFileFilter(SnapshotFileType.EPS));
+        SNAPSHOT_TYPE_TO_FILTER.put(SnapshotFileType.SVG,
+                new SnapshotFileFilter(SnapshotFileType.SVG));
         SNAPSHOT_TYPE_TO_FILTER.put(SnapshotFileType.PNG,
                 new SnapshotFileFilter(SnapshotFileType.PNG));
     }
@@ -75,7 +77,7 @@ public class GenericUtilities {
      * Creates a device compatible buffered image.
      *
      * @param width  the image width in pixels
-     * @param weight the image height in pixels
+     * @param height the image height in pixels
      */
     public static BufferedImage getDeviceCompatibleImage(int width, int height) {
 
@@ -209,7 +211,7 @@ public class GenericUtilities {
             }
 
             return file.getName().toLowerCase().endsWith(
-                    IGVConstants.GENOME_FILE_EXTENSION);
+                    Globals.GENOME_FILE_EXTENSION);
         }
 
         public String getDescription() {
@@ -217,42 +219,72 @@ public class GenericUtilities {
         }
 
         public String getExtension() {
-            return IGVConstants.GENOME_FILE_EXTENSION;
+            return Globals.GENOME_FILE_EXTENSION;
         }
     }
 
-    public static void doComponentSnapshot(Component component, File file) {
-        doComponentSnapshot(component, file, SnapshotFileType.PDF);
-    }
 
-    public static void doComponentSnapshot(Component component, File file,
-                                           SnapshotFileType type) {
+    public static void doComponentSnapshot(Component component, File file, SnapshotFileType type) {
 
 
         int width = component.getWidth();
         int height = component.getHeight();
 
         // Call appropriate converter
-        if (type.equals(SnapshotFileType.JPEG)) {
-            //GenericUtilities.generateJpegUsingJava2D(component, file, width,
-            //                                         height);
-            exportScreenShotJPEG(component, file, width, height);
-        } else if (type.equals(SnapshotFileType.EPS)) {
-            //GenericUtilities.generateEpsUsingJava2D(component, file, width,
-            //                                        height);
-            exportScreenShotEPS(component, file, width, height);
-        } else if (type.equals(SnapshotFileType.PNG)) {
-            //GenericUtilities.generatePngUsingJava2D(component, file, width,
-            //                                        height);
-            exportScreenShotPNG(component, file, width, height);
+        switch (type) {
+            case JPEG:
+                exportScreenShotJPEG(component, file, width, height);
+                break;
+            //case EPS:
+            //    exportScreenShotEPS(component, file, width, height);
+            //    break;
+            case PNG:
+                exportScreenShotPNG(component, file, width, height);
+                break;
+            case SVG:
+                logger.debug("Exporting svg screenshot");
+                exportScreenshotSVG(component, file, width, height);
+                break;
+
         }
 
+
+    }
+
+    private static void exportScreenshotSVG(Component target, File selecteddFile, int width, int height) {
+        // Get a DOMImplementation.
+        try {
+            logger.info("Getting dom");
+            DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
+
+
+            // Create an instance of org.w3c.dom.Document.
+            String svgNS = "http://www.w3.org/2000/svg";
+            Document document = domImpl.createDocument(svgNS, "svg", null);
+
+
+            // Create an instance of the SVG Generator.
+            SVGGraphics2D svgGenerator = new SVGGraphics2D(document);
+            //logger.info("Painting");
+            target.paintAll(svgGenerator);
+
+            // Finally, stream out SVG to the standard output using
+            // UTF-8 encoding.
+            boolean useCSS = true; // we want to use CSS style attributes
+            Writer out = new BufferedWriter(new FileWriter(selecteddFile));
+            //logger.info("Writing output");
+            svgGenerator.stream(out, useCSS);
+            //logger.info("Done");
+        } catch (Exception e) {
+            logger.error("Error creating SVG file", e);
+            MessageUtils.showMessage("Error encountered creating SVG file: " + e.toString());
+        }
     }
 
     private static void exportScreenShotJPEG(Component target, File selectedFile,
                                              int width, int height) {
 
-        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_INDEXED);
+        BufferedImage image = getDeviceCompatibleImage(width, height);
         Graphics g = image.createGraphics();
         target.paintAll(g);
 
@@ -266,8 +298,8 @@ public class GenericUtilities {
         }
     }
 
-    private static void exportScreenShotEPS(Component target, File selectedFile,
-                                            int pageWidth, int pageHeight) {
+    /*
+    private static void exportScreenShotEPS(Component target, File selectedFile, int pageWidth, int pageHeight) {
 
         Graphics2D epsG = new EpsGraphics2D();
 
@@ -279,7 +311,7 @@ public class GenericUtilities {
         // Clip to document size
         epsG.clipRect(0, 0, pageWidth, pageHeight);
 
-        target.paint(epsG);
+        target.paintAll(epsG);
 
         PrintWriter writer = null;
 
@@ -294,11 +326,12 @@ public class GenericUtilities {
             }
         }
     }
+    */
 
     private static void exportScreenShotPNG(Component target, File selectedFile,
                                             int width, int height) {
 
-        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_INDEXED);
+        BufferedImage image = getDeviceCompatibleImage(width, height);
         Graphics g = image.createGraphics();
         target.paintAll(g);
 
diff --git a/src/org/broad/igv/ui/util/HistoryMenu.java b/src/org/broad/igv/ui/util/HistoryMenu.java
new file mode 100644
index 0000000..3e6a453
--- /dev/null
+++ b/src/org/broad/igv/ui/util/HistoryMenu.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.ui.util;
+
+import org.broad.igv.session.ViewContext;
+import org.broad.igv.ui.IGVMainFrame;
+import org.broad.igv.ui.action.SearchCommand;
+
+import javax.swing.*;
+import javax.swing.event.MenuEvent;
+import javax.swing.event.MenuListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author jrobinso
+ * @date Sep 1, 2010
+ */
+public class HistoryMenu extends JMenu {
+    static int MAX_ITEMS = 30;
+    private JMenuItem backItem;
+    private JMenuItem forwardItem;
+    private JMenuItem clearAllItem;
+
+    public HistoryMenu() {
+        this("History");
+    }
+
+
+    public HistoryMenu(String name) {
+        super(name);
+        final ViewContext.History history = ViewContext.getInstance().history;
+
+        clearAllItem = new JMenuItem("Clear all");
+        clearAllItem.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent actionEvent) {
+                history.clear();
+            }
+        });
+
+        backItem = new JMenuItem("Back");
+        backItem.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent actionEvent) {
+                String locus = history.back();
+                if (locus != null) {
+                    (new SearchCommand(ViewContext.getInstance(), locus, false)).execute();
+                    IGVMainFrame.getInstance().refreshCommandBar();
+
+                }
+            }
+        });
+
+        forwardItem = new JMenuItem("Forward");
+        forwardItem.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent actionEvent) {
+                String locus = history.forward();
+                if (locus != null) {
+                    (new SearchCommand(ViewContext.getInstance(), locus, false)).execute();
+                    IGVMainFrame.getInstance().refreshCommandBar();
+                }
+            }
+        });
+
+
+        this.addMenuListener(new MenuListener() {
+            public void menuSelected(MenuEvent menuEvent) {
+
+                final ViewContext.History history = ViewContext.getInstance().history;
+
+
+                List<String> allLoci = ViewContext.getInstance().getAllHistory();
+                boolean hasBack = history.peekBack() != null;
+                boolean hasForward = history.peekForward() != null;
+                backItem.setEnabled(hasBack);
+                forwardItem.setEnabled(hasForward);
+                clearAllItem.setEnabled(allLoci.size() > 0);
+
+                // Update history list
+                removeAll();
+
+                add(backItem);
+                add(forwardItem);
+                addSeparator();
+
+                int nItems = 0;
+                // Do in reverse order
+
+                for (int idx = allLoci.size() - 1; idx >= 0; idx--) {
+                    String s = allLoci.get(idx);
+                    final JMenuItem item = new JMenuItem(s);
+                    item.addActionListener(new ActionListener() {
+                        public void actionPerformed(ActionEvent actionEvent) {
+                            String locus = item.getText();
+                            (new SearchCommand(ViewContext.getInstance(), locus, false)).execute();
+                            IGVMainFrame.getInstance().refreshCommandBar();
+                        }
+                    });
+                    add(item);
+                    if (nItems++ > MAX_ITEMS) {
+                        break;
+                    }
+                }
+
+               
+                addSeparator();
+                add(clearAllItem);
+
+
+            }
+
+            public void menuDeselected(MenuEvent menuEvent) {
+                //To change body of implemented methods use File | Settings | File Templates.
+            }
+
+            public void menuCanceled(MenuEvent menuEvent) {
+                //To change body of implemented methods use File | Settings | File Templates.
+            }
+        });
+    }
+
+
+}
diff --git a/src/org/broad/igv/ui/util/IconFactory.java b/src/org/broad/igv/ui/util/IconFactory.java
index 5edfe8a..f99170f 100644
--- a/src/org/broad/igv/ui/util/IconFactory.java
+++ b/src/org/broad/igv/ui/util/IconFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/util/IndefiniteProgressMonitor.java b/src/org/broad/igv/ui/util/IndefiniteProgressMonitor.java
index c3787d9..4af8dbb 100644
--- a/src/org/broad/igv/ui/util/IndefiniteProgressMonitor.java
+++ b/src/org/broad/igv/ui/util/IndefiniteProgressMonitor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,8 +22,6 @@
  */
 package org.broad.igv.ui.util;
 
-import org.broad.igv.ui.GuiUtilities;
-
 import java.util.Timer;
 import java.util.TimerTask;
 
@@ -62,7 +60,7 @@ public class IndefiniteProgressMonitor extends ProgressMonitor {
         public void run() {
 
 
-            GuiUtilities.invokeOnEventThread(new Runnable() {
+            UIUtilities.invokeOnEventThread(new Runnable() {
                 public void run() {
                     fireProgressChange(progressIncrement);
                 }
diff --git a/src/org/broad/igv/ui/util/LinkCheckBox.java b/src/org/broad/igv/ui/util/LinkCheckBox.java
index 8b93a2a..8d298a9 100644
--- a/src/org/broad/igv/ui/util/LinkCheckBox.java
+++ b/src/org/broad/igv/ui/util/LinkCheckBox.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/util/LinkLabel.form b/src/org/broad/igv/ui/util/LinkLabel.form
deleted file mode 100644
index 878da69..0000000
--- a/src/org/broad/igv/ui/util/LinkLabel.form
+++ /dev/null
@@ -1,85 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-
-<Form version="1.3" maxVersion="1.6" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
-  <Properties>
-    <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-      <Dimension value="[32767, 25]"/>
-    </Property>
-    <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-      <Dimension value="[25, 25]"/>
-    </Property>
-    <Property name="requestFocusEnabled" type="boolean" value="false"/>
-  </Properties>
-  <AuxValues>
-    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
-    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="2"/>
-    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
-    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
-    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,126,68,0,0,127,-1"/>
-  </AuxValues>
-
-  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout">
-    <Property name="alignment" type="int" value="0"/>
-    <Property name="horizontalGap" type="int" value="2"/>
-    <Property name="verticalGap" type="int" value="0"/>
-  </Layout>
-  <SubComponents>
-    <Component class="javax.swing.JLabel" name="jLabel1">
-      <Properties>
-        <Property name="horizontalAlignment" type="int" value="2"/>
-        <Property name="text" type="java.lang.String" value="Text"/>
-        <Property name="verticalAlignment" type="int" value="1"/>
-        <Property name="horizontalTextPosition" type="int" value="2"/>
-        <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-          <Dimension value="[32767, 22]"/>
-        </Property>
-        <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-          <Dimension value="[22, 22]"/>
-        </Property>
-        <Property name="verticalTextPosition" type="int" value="1"/>
-      </Properties>
-    </Component>
-    <Component class="javax.swing.JButton" name="jButton1">
-      <Properties>
-        <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-          <Image iconType="3" name="/toolbarButtonGraphics/general/Information16.gif"/>
-        </Property>
-        <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
-          <Border info="null"/>
-        </Property>
-        <Property name="borderPainted" type="boolean" value="false"/>
-        <Property name="focusPainted" type="boolean" value="false"/>
-        <Property name="horizontalAlignment" type="int" value="2"/>
-        <Property name="horizontalTextPosition" type="int" value="2"/>
-        <Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
-          <Insets value="[0, 0, 0, 0]"/>
-        </Property>
-        <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-          <Dimension value="[15, 15]"/>
-        </Property>
-        <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-          <Dimension value="[15, 15]"/>
-        </Property>
-        <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-          <Dimension value="[15, 15]"/>
-        </Property>
-        <Property name="rolloverEnabled" type="boolean" value="false"/>
-        <Property name="selectedIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-          <Image iconType="3" name="/toolbarButtonGraphics/general/Information16.gif"/>
-        </Property>
-        <Property name="verticalAlignment" type="int" value="1"/>
-        <Property name="verticalTextPosition" type="int" value="1"/>
-      </Properties>
-      <Events>
-        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton1ActionPerformed"/>
-      </Events>
-      <AuxValues>
-        <AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new JideButton();"/>
-      </AuxValues>
-    </Component>
-  </SubComponents>
-</Form>
diff --git a/src/org/broad/igv/ui/util/LinkLabel.java b/src/org/broad/igv/ui/util/LinkLabel.java
deleted file mode 100644
index bd4b596..0000000
--- a/src/org/broad/igv/ui/util/LinkLabel.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-package org.broad.igv.ui.util;
-
-import com.jidesoft.swing.JideButton;
-import org.apache.log4j.Logger;
-import org.broad.igv.util.BrowserLauncher;
-
-import java.awt.*;
-import java.io.IOException;
-
-/*
- * LinkLabel.java
- *
- * Created on August 18, 2008, 11:27 AM
- */
-
-
-/**
- * @author eflakes
- */
-public class LinkLabel extends javax.swing.JPanel {
-
-    private static Logger log = Logger.getLogger(LinkLabel.class);
-    private String hyperLink;
-
-    /**
-     * Creates new form LinkLabel
-     */
-    public LinkLabel() {
-        initComponents();
-        setOpaque(true);
-    }
-
-    public void showHyperLink(boolean value) {
-
-        if (jButton1 != null) {
-            jButton1.setVisible(value);
-        }
-    }
-
-    @Override
-    public void setEnabled(boolean value) {
-        super.setEnabled(value);
-
-        if (jLabel1 != null) {
-            jLabel1.setEnabled(value);
-        }
-    }
-
-    public void setText(String value) {
-        if (jLabel1 != null) {
-            jLabel1.setText(value);
-        }
-    }
-
-    public String getText() {
-        if (jLabel1 != null) {
-            return jLabel1.getText();
-        } else {
-            return null;
-        }
-    }
-
-    public void setHyperLink(String value) {
-        hyperLink = value;
-    }
-
-    public String getHyperLink() {
-        return hyperLink;
-    }
-
-    @Override
-    public void setBackground(Color value) {
-        super.setBackground(value);
-        if (jLabel1 != null) {
-            jLabel1.setBackground(value);
-        }
-    }
-
-    @Override
-    public void setForeground(Color value) {
-        super.setForeground(value);
-        if (jLabel1 != null) {
-            jLabel1.setForeground(value);
-        }
-    }
-
-    /**
-     * This method is called from within the constructor to
-     * initialize the form.
-     * WARNING: Do NOT modify this code. The content of this method is
-     * always regenerated by the Form Editor.
-     */
-    @SuppressWarnings("unchecked")
-    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
-    private void initComponents() {
-
-        jLabel1 = new javax.swing.JLabel();
-        jButton1 = new JideButton();
-
-        setMaximumSize(new java.awt.Dimension(32767, 25));
-        setMinimumSize(new java.awt.Dimension(25, 25));
-        setRequestFocusEnabled(false);
-        setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT, 2, 0));
-
-        jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
-        jLabel1.setText("Text");
-        jLabel1.setVerticalAlignment(javax.swing.SwingConstants.TOP);
-        jLabel1.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);
-        jLabel1.setMaximumSize(new java.awt.Dimension(32767, 22));
-        jLabel1.setMinimumSize(new java.awt.Dimension(22, 22));
-        jLabel1.setVerticalTextPosition(javax.swing.SwingConstants.TOP);
-        add(jLabel1);
-
-        jButton1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/toolbarButtonGraphics/general/Information16.gif"))); // NOI18N
-        jButton1.setBorder(null);
-        jButton1.setBorderPainted(false);
-        jButton1.setFocusPainted(false);
-        jButton1.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
-        jButton1.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);
-        jButton1.setMargin(new java.awt.Insets(0, 0, 0, 0));
-        jButton1.setMaximumSize(new java.awt.Dimension(15, 15));
-        jButton1.setMinimumSize(new java.awt.Dimension(15, 15));
-        jButton1.setPreferredSize(new java.awt.Dimension(15, 15));
-        jButton1.setRolloverEnabled(false);
-        jButton1.setSelectedIcon(new javax.swing.ImageIcon(getClass().getResource("/toolbarButtonGraphics/general/Information16.gif"))); // NOI18N
-        jButton1.setVerticalAlignment(javax.swing.SwingConstants.TOP);
-        jButton1.setVerticalTextPosition(javax.swing.SwingConstants.TOP);
-        jButton1.addActionListener(new java.awt.event.ActionListener() {
-            public void actionPerformed(java.awt.event.ActionEvent evt) {
-                jButton1ActionPerformed(evt);
-            }
-        });
-        add(jButton1);
-    }// </editor-fold>//GEN-END:initComponents
-
-    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
-        try {
-            BrowserLauncher.openURL(hyperLink);
-        }
-        catch (IOException e) {
-            log.error("Error launching from hyperlink", e);
-        }
-    }//GEN-LAST:event_jButton1ActionPerformed
-
-
-    // Variables declaration - do not modify//GEN-BEGIN:variables
-    private javax.swing.JButton jButton1;
-    private javax.swing.JLabel jLabel1;
-    // End of variables declaration//GEN-END:variables
-
-}
diff --git a/src/org/broad/igv/ui/util/MenuAndToolbarUtils.java b/src/org/broad/igv/ui/util/MenuAndToolbarUtils.java
index 23f2037..03092d4 100644
--- a/src/org/broad/igv/ui/util/MenuAndToolbarUtils.java
+++ b/src/org/broad/igv/ui/util/MenuAndToolbarUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/util/MessageUtils.java b/src/org/broad/igv/ui/util/MessageUtils.java
index cadfeec..d22e71a 100644
--- a/src/org/broad/igv/ui/util/MessageUtils.java
+++ b/src/org/broad/igv/ui/util/MessageUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -22,24 +22,78 @@
  */
 package org.broad.igv.ui.util;
 
-import org.broad.igv.IGVConstants;
+import org.apache.log4j.Logger;
+import org.broad.igv.Globals;
 import org.broad.igv.ui.IGVMainFrame;
 
 import javax.swing.*;
+import java.awt.*;
 
 /**
  * @author jrobinso
  */
 public class MessageUtils {
 
+    private static Logger log = Logger.getLogger(MessageUtils.class);
+
     public static void showMessage(String message) {
 
-        if (IGVConstants.isHeadless()) {
-            System.out.println(message);
+        if (Globals.isHeadless() || Globals.isSuppress()) {
+            log.info(message);
         } else {
-            JOptionPane.showMessageDialog(
-                    IGVMainFrame.getInstance(),
-                    message);
+            JOptionPane.showMessageDialog(IGVMainFrame.getInstance(), message);
+        }
+    }
+
+    public static boolean confirm(String message) {
+
+        int opt = JOptionPane.showConfirmDialog(
+                IGVMainFrame.getInstance(), message, "Continue Loading", JOptionPane.YES_NO_OPTION);
+        return opt == JOptionPane.YES_OPTION;
+
+    }
+
+    /**
+     * Method description
+     *
+     * @param component
+     * @param message
+     * @param log
+     * @param e
+     */
+    public static void showAndLogErrorMessage(final Component component, final String message,
+                                              final Logger log, final Exception e) {
+        if (log != null) {
+
+            if (e != null) {
+                log.error(message, e);
+            } else {
+                log.error(message);
+            }
         }
+        JOptionPane.showMessageDialog(component, message);
+
+    }
+
+    /**
+     * Method description
+     *
+     * @param component
+     * @param message
+     * @param log
+     */
+    public static void showAndLogErrorMessage(Component component, String message, Logger log) {
+
+        showAndLogErrorMessage(component, message, log, null);
+    }
+
+    public static String showInputDialog(String message, String defaultValue) {
+        String val = JOptionPane.showInputDialog(IGVMainFrame.getInstance(), message, defaultValue);
+        return val;
+    }
+
+        public static String showInputDialog(String message) {
+        String val = JOptionPane.showInputDialog(IGVMainFrame.getInstance(), message);
+        return val;
     }
 }
diff --git a/src/org/broad/igv/ui/util/OkCancelDialog.java b/src/org/broad/igv/ui/util/OkCancelDialog.java
index f8f9ca7..aafe0ec 100644
--- a/src/org/broad/igv/ui/util/OkCancelDialog.java
+++ b/src/org/broad/igv/ui/util/OkCancelDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/util/Packable.java b/src/org/broad/igv/ui/util/Packable.java
index ba6cc24..7d97c5d 100644
--- a/src/org/broad/igv/ui/util/Packable.java
+++ b/src/org/broad/igv/ui/util/Packable.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/util/ProgressBar.java b/src/org/broad/igv/ui/util/ProgressBar.java
index 69d55ce..6b45f8c 100644
--- a/src/org/broad/igv/ui/util/ProgressBar.java
+++ b/src/org/broad/igv/ui/util/ProgressBar.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/util/ProgressMonitor.java b/src/org/broad/igv/ui/util/ProgressMonitor.java
index 966f2f1..8b0d9ee 100644
--- a/src/org/broad/igv/ui/util/ProgressMonitor.java
+++ b/src/org/broad/igv/ui/util/ProgressMonitor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/util/PropertyDialog.java b/src/org/broad/igv/ui/util/PropertyDialog.java
index a9d30c1..3e77600 100644
--- a/src/org/broad/igv/ui/util/PropertyDialog.java
+++ b/src/org/broad/igv/ui/util/PropertyDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -25,7 +25,6 @@ package org.broad.igv.ui.util;
 
 //~--- non-JDK imports --------------------------------------------------------
 
-import org.broad.igv.ui.GuiUtilities;
 import org.broad.igv.ui.UIConstants;
 import org.broad.igv.ui.WaitCursorManager;
 
@@ -317,7 +316,7 @@ public class PropertyDialog extends OkCancelDialog {
                 }
             });
 
-            GuiUtilities.invokeOnEventThread(new Runnable() {
+            UIUtilities.invokeOnEventThread(new Runnable() {
                 public void run() {
                     ColorPanel.this.setToolTipText(UIConstants.CLICK_ITEM_TO_EDIT_TOOLTIP);
                 }
diff --git a/src/org/broad/igv/ui/util/PropertyManager.java b/src/org/broad/igv/ui/util/PropertyManager.java
index f9be8df..4954fca 100644
--- a/src/org/broad/igv/ui/util/PropertyManager.java
+++ b/src/org/broad/igv/ui/util/PropertyManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/util/SortDialog.java b/src/org/broad/igv/ui/util/SortDialog.java
index 433c9bb..2868003 100644
--- a/src/org/broad/igv/ui/util/SortDialog.java
+++ b/src/org/broad/igv/ui/util/SortDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/util/Tool.java b/src/org/broad/igv/ui/util/Tool.java
index e21c5bd..372c554 100644
--- a/src/org/broad/igv/ui/util/Tool.java
+++ b/src/org/broad/igv/ui/util/Tool.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/util/TracingRepaintManager.java b/src/org/broad/igv/ui/util/TracingRepaintManager.java
index 6815598..6042bba 100644
--- a/src/org/broad/igv/ui/util/TracingRepaintManager.java
+++ b/src/org/broad/igv/ui/util/TracingRepaintManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/ui/util/UIUtilities.java b/src/org/broad/igv/ui/util/UIUtilities.java
index 116eba4..4b46bdb 100644
--- a/src/org/broad/igv/ui/util/UIUtilities.java
+++ b/src/org/broad/igv/ui/util/UIUtilities.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,7 +23,6 @@ package org.broad.igv.ui.util;
 
 //~--- non-JDK imports --------------------------------------------------------
 
-import org.apache.log4j.Logger;
 import org.broad.igv.ui.IGVMainFrame;
 
 import javax.swing.*;
@@ -116,55 +115,6 @@ public class UIUtilities {
     /**
      * Method description
      *
-     * @param component
-     * @param message
-     */
-    public static void showErrorMessage(Component component, final String message) {
-        showAndLogErrorMessage(component, message, null);
-    }
-
-    /**
-     * Method description
-     *
-     * @param component
-     * @param message
-     * @param log
-     * @param e
-     */
-    public static void showAndLogErrorMessage(final Component component, final String message,
-                                              final Logger log, final Exception e) {
-        if (log != null) {
-
-            if (e != null) {
-                log.error(message, e);
-            } else {
-                log.error(message);
-            }
-        }
-        JOptionPane.showMessageDialog(component, message);
-
-    }
-
-    public static void showMessageDialog(final String message) {
-        JOptionPane.showMessageDialog(IGVMainFrame.getInstance(), message);
-    }
-
-
-    /**
-     * Method description
-     *
-     * @param component
-     * @param message
-     * @param log
-     */
-    public static void showAndLogErrorMessage(Component component, String message, Logger log) {
-
-        showAndLogErrorMessage(component, message, log, null);
-    }
-
-    /**
-     * Method description
-     *
      * @param dialogTitle
      * @param defaultColor
      * @return
@@ -249,4 +199,21 @@ public class UIUtilities {
         window.setLocation(x, y);
         window.requestFocus();
     }
+
+    /**
+     * A wrapper around invokeOnEventThread.  If the runnable is already in the event dispatching
+     * queue it is just run.  Otherwise it is placed in the queue via invokeOnEventThread.
+     * <p/>
+     * I'm not sure this is strictly neccessary,  but is safe.
+     *
+     * @param runnable
+     */
+    public static void invokeOnEventThread(Runnable runnable) {
+        if (SwingUtilities.isEventDispatchThread()) {
+            runnable.run();
+        } else {
+            SwingUtilities.invokeLater(runnable);
+        }
+
+    }
 }
diff --git a/src/org/broad/igv/util/AlphaColorGradient.java b/src/org/broad/igv/util/AlphaColorGradient.java
index 7db6bac..7ecec75 100644
--- a/src/org/broad/igv/util/AlphaColorGradient.java
+++ b/src/org/broad/igv/util/AlphaColorGradient.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/ArrayHeapIntSorter.java b/src/org/broad/igv/util/ArrayHeapIntSorter.java
index 183da76..bc22f7c 100644
--- a/src/org/broad/igv/util/ArrayHeapIntSorter.java
+++ b/src/org/broad/igv/util/ArrayHeapIntSorter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/ArrayHeapObjectSorter.java b/src/org/broad/igv/util/ArrayHeapObjectSorter.java
index 78a529b..c4c9639 100644
--- a/src/org/broad/igv/util/ArrayHeapObjectSorter.java
+++ b/src/org/broad/igv/util/ArrayHeapObjectSorter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/AsciiEcho.java b/src/org/broad/igv/util/AsciiEcho.java
index 06a8ef8..ccde2ed 100644
--- a/src/org/broad/igv/util/AsciiEcho.java
+++ b/src/org/broad/igv/util/AsciiEcho.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/AsciiLineReader.java b/src/org/broad/igv/util/AsciiLineReader.java
index 661658d..dd4f51c 100644
--- a/src/org/broad/igv/util/AsciiLineReader.java
+++ b/src/org/broad/igv/util/AsciiLineReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -152,31 +152,33 @@ public class AsciiLineReader {
     }
 
     public static void main(String[] args) throws Exception {
-        File testFile = new File("/Users/jrobinso/projects/client/test/data/gc5_10.wig");
+        File testFile = new File("test/data/HindForGISTIC.hg16.cn");
         long t0, lineCount, dt;
         double rate;
 
-        BufferedReader reader2 = new BufferedReader(new FileReader(testFile));
-        t0 = System.currentTimeMillis();
-        lineCount = 0;
-        while (reader2.readLine() != null) {
-            lineCount++;
-        }
-        dt = System.currentTimeMillis() - t0;
-        rate = ((double) lineCount) / dt;
-        System.out.println("BR: " + lineCount + " lines read.  Rate = " + rate + " lines per second.   DT = " + dt);
-        reader2.close();
-
-        AsciiLineReader reader = new AsciiLineReader(new FileInputStream(testFile));
-        t0 = System.currentTimeMillis();
-        lineCount = 0;
-        while (reader.readLine() != null) {
-            lineCount++;
+        for (int i = 0; i < 3; i++) {
+            BufferedReader reader2 = new BufferedReader(new FileReader(testFile));
+            t0 = System.currentTimeMillis();
+            lineCount = 0;
+            while (reader2.readLine() != null) {
+                lineCount++;
+            }
+            dt = System.currentTimeMillis() - t0;
+            rate = ((double) lineCount) / dt;
+            System.out.println("BR: " + lineCount + " lines read.  Rate = " + rate + " lines per second.   DT = " + dt);
+            reader2.close();
+
+            AsciiLineReader reader = new AsciiLineReader(new FileInputStream(testFile));
+            t0 = System.currentTimeMillis();
+            lineCount = 0;
+            while (reader.readLine() != null) {
+                lineCount++;
+            }
+            dt = System.currentTimeMillis() - t0;
+            rate = ((double) lineCount) / dt;
+            System.out.println("AR: " + lineCount + " lines read.  Rate = " + rate + " lines per second.     DT = " + dt);
+            reader.close();
         }
-        dt = System.currentTimeMillis() - t0;
-        rate = ((double) lineCount) / dt;
-        System.out.println("AR: " + lineCount + " lines read.  Rate = " + rate + " lines per second.     DT = " + dt);
-        reader.close();
 
 
     }
diff --git a/src/org/broad/igv/util/BinaryReader.java b/src/org/broad/igv/util/BinaryReader.java
deleted file mode 100644
index b00aadc..0000000
--- a/src/org/broad/igv/util/BinaryReader.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * BinaryParser.java
- *
- * Created on July 26, 2007, 1:01 AM
- *
- * To change this template, choose Tools | Template Manager
- * and open the template in the editor.
- */
-
-package org.broad.igv.util;
-
-
-import org.j3d.io.EndianConverter;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.List;
-
-/**
- * @author jrobinso
- */
-public abstract class BinaryReader {
-
-    private boolean littleEndian = false;
-
-    /**
-     * Creates a new instance of BinaryParser
-     */
-    public BinaryReader() {
-    }
-
-    public BinaryReader(boolean littleEndian) {
-        this.setLittleEndian(littleEndian);
-    }
-
-    /**
-     * Read an array of floats from a  binary file.   It is assumed that the
-     * file contains floats written with DataOutput.writeFloat();
-     */
-
-    public float[] readFloats(File file) {
-        return convertBytesToFloats(readBytes(file));
-    }
-
-    /**
-     * Read an array of ints from the binary file.   It is assumed that the
-     * file contains ints written with DataOutput.writeInt();
-     */
-    /**
-     * Read an array of ints from the binary file.   It is assumed that the
-     * file contains ints written with DataOutput.writeInt();
-     */
-    public int[] readInts(File file) {
-        return ByteConverter.convertBytesToInts(readBytes(file));
-    }
-
-
-    /**
-     * Read an array of strings from the binary file.   It is assumed that the
-     * file contains fixed length ascii arrays of nChars characters each.
-     */
-    public String[] readStrings(File file, int nChars) {
-        return ByteConverter.convertBytesToStrings(readBytes(file), nChars);
-    }
-
-    /**
-     * Read an array of longs from the binary file.   It is assumed that the
-     * file contains longs written with DataOutput.writeInt();
-     */
-    public long[] readLongs(File file) {
-        return ByteConverter.convertBytesToLongs(readBytes(file));
-    }
-
-
-    /**
-     * Read a selection of floats from the given file.
-     * start -  logical position in file to start reading.  The logical
-     * position is related to the file position as follows
-     * file position = logical position X  4.
-     * <p/>
-     * stop  -  logical position in file to stop (should be <= file length)
-     * <p/>
-     * freq - how often to read.
-     * <p/>
-     * Example -- to read every 5th float enter a readSize=4,  freq=5
-     */
-    public float[] sampleFloats(File inputFile, int start, int stop, int freq) throws IOException {
-        byte[] bytes = sampleBytes(inputFile, start, stop, (Float.SIZE / 8), freq);
-        return convertBytesToFloats(bytes);
-    }
-
-    /**
-     * Read a selection of longs from the given file.
-     * start -  logical position in file to start reading.  The logical
-     * position is related to the file position as follows
-     * file position = logical position X  4.
-     * <p/>
-     * stop  -  logical position in file to stop (should be <= file length)
-     * <p/>
-     * freq - how often to read.
-     * <p/>
-     * Example -- to read every 5th float enter a readSize=4,  freq=5
-     */
-    public long[] sampleLongs(File inputFile, int start, int stop, int freq) throws IOException {
-        byte[] bytes = sampleBytes(inputFile, start, stop, (Long.SIZE / 8), freq);
-        return ByteConverter.convertBytesToLongs(bytes);
-    }
-
-
-    /**
-     * Read a selection of longs from the given file.
-     * start -  logical position in file to start reading.  The logical
-     * position is related to the file position as follows
-     * file position = logical position X  4.
-     * <p/>
-     * stop  -  logical position in file to stop (should be <= file length)
-     * <p/>
-     * freq - how often to read.
-     * <p/>
-     * Example -- to read every 5th float enter a readSize=4,  freq=5
-     */
-    public String[] sampleStrings(File inputFile, int start, int stop, int freq, int nChars) throws IOException {
-
-        int stringSizeInBytes = nChars * (Character.SIZE / 8);
-        byte[] bytes = sampleBytes(inputFile, start, stop, stringSizeInBytes, freq);
-        return ByteConverter.convertBytesToStrings(bytes, nChars);
-    }
-
-
-    /**
-     * Read a selection of sampledBytes from the given file.
-     * start -  logical position in file to start reading.  The logical
-     * position is related to the file position as follows
-     * file position = logical position X  readSize.
-     * <p/>
-     * stop  -  logical position in file to stop (should be <= file length)
-     * <p/>
-     * readSize - number of sampledBytes for a single read (4 sampledBytes for floats)
-     * <p/>
-     * freq - how often to read.
-     * <p/>
-     * Example -- to read every 5th float enter a readSize=4,  freq=5
-     */
-    protected abstract byte[] sampleBytes(File inputFile, int start, int stop, int readSize, int freq) throws IOException;
-
-    /**
-     * Read all bytes in the specified file.  Implementation is the responsibility
-     * of subclasses.
-     */
-    protected abstract byte[] readBytes(File file);
-
-    public abstract List<File> getDataFiles(String dataDirectory);
-
-    /**
-     * Read the number of bytes specificed from the input stream
-     */
-    protected byte[] readBytes(InputStream inStream, int nBytes) throws IOException {
-        byte[] bytes = new byte[nBytes];
-        int bytesRead = 0;
-        while (bytesRead < nBytes) {
-            bytesRead += inStream.read(bytes, bytesRead, inStream.available());
-        }
-        return bytes;
-    }
-
-    /**
-     * Convert bytes to floats.  Handles endian conversion
-     */
-    private float[] convertBytesToFloats(byte[] bytes) {
-        if (littleEndian) {
-            float[] floats = new float[bytes.length / (Float.SIZE / 8)];
-            EndianConverter.convertLittleEndianToFloat(bytes, floats, bytes.length, 0, floats.length);
-            return floats;
-        } else {
-            return ByteConverter.convertBytesToFloats(bytes);
-        }
-    }
-
-    public void setLittleEndian(boolean littleEndian) {
-        this.littleEndian = littleEndian;
-    }
-
-}
diff --git a/src/org/broad/igv/util/BrowserLauncher.java b/src/org/broad/igv/util/BrowserLauncher.java
index 747395f..198ee49 100644
--- a/src/org/broad/igv/util/BrowserLauncher.java
+++ b/src/org/broad/igv/util/BrowserLauncher.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/ByteConverter.java b/src/org/broad/igv/util/ByteConverter.java
index 7cc8740..ddeb569 100644
--- a/src/org/broad/igv/util/ByteConverter.java
+++ b/src/org/broad/igv/util/ByteConverter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/ChromosomeColors.java b/src/org/broad/igv/util/ChromosomeColors.java
index 55d3443..ee76755 100644
--- a/src/org/broad/igv/util/ChromosomeColors.java
+++ b/src/org/broad/igv/util/ChromosomeColors.java
@@ -49,7 +49,8 @@ public class ChromosomeColors {
         colorMap.put("chrX", new Color(204, 153, 0));
         colorMap.put("chrY", new Color(153, 204, 0));
         colorMap.put("chrUn", Color.DARK_GRAY);
-        colorMap.put("chr1", new Color(139, 155, 187));
+        //colorMap.put("chr1", new Color(139, 155, 187));
+        colorMap.put("chr1", Color.red);
         colorMap.put("chrI", new Color(139, 155, 187));
         colorMap.put("chr2", new Color(206, 61, 50));
         colorMap.put("chrII", new Color(206, 61, 50));
diff --git a/src/org/broad/igv/util/ColorUtilities.java b/src/org/broad/igv/util/ColorUtilities.java
index 31967db..c52c0dc 100644
--- a/src/org/broad/igv/util/ColorUtilities.java
+++ b/src/org/broad/igv/util/ColorUtilities.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -24,6 +24,7 @@ package org.broad.igv.util;
 import org.apache.log4j.Logger;
 
 import java.awt.*;
+import java.util.HashMap;
 
 
 /**
@@ -36,6 +37,10 @@ public class ColorUtilities {
 
     private static Logger log = Logger.getLogger(ColorUtilities.class);
 
+    public static HashMap colorMap = new HashMap(1000);
+
+    private static float[] whiteComponents = Color.white.getRGBColorComponents(null);
+
     /**
      * Method description
      *
@@ -134,10 +139,10 @@ public class ColorUtilities {
                 int blue = Integer.parseInt(rgb[2]);
                 return new Color(red, green, blue);
             } else {
-                if(string.startsWith("#")) {
+                if (string.startsWith("#")) {
                     string = string.substring(1);
                 }
-                if(string.length() == 6) {
+                if (string.length() == 6) {
                     int red = Integer.parseInt(string.substring(0, 2), 16);
                     int green = Integer.parseInt(string.substring(2, 4), 16);
                     int blue = Integer.parseInt(string.substring(4, 6), 16);
@@ -154,4 +159,42 @@ public class ColorUtilities {
     }
 
 
+    /**
+     * Return  alphas shaded color.  This method is used, rather than the Color constructor, so that
+     * the alpha is not lost in postscript output.
+     *
+     * @param dest
+     * @param source
+     * @param alpha
+     * @return
+     */
+    public static Color getCompositeColor(float[] dest, float[] source, float alpha) {
+        int r = (int) ((alpha * source[0] + (1 - alpha) * dest[0]) * 255 + 0.5);
+        int g = (int) ((alpha * source[1] + (1 - alpha) * dest[1]) * 255 + 0.5);
+        int b = (int) ((alpha * source[2] + (1 - alpha) * dest[2]) * 255 + 0.5);
+        int a = 255;
+        int value = ((a & 0xFF) << 24) |
+                ((r & 0xFF) << 16) |
+                ((g & 0xFF) << 8) |
+                ((b & 0xFF) << 0);
+
+        Color c = (Color) colorMap.get(value);
+        if (c == null) {
+            c = new Color(value);
+            colorMap.put(value, c);
+        }
+        return c;
+    }
+
+    /**
+     * Return  alphas shaded color for a white background.  This method is used, rather than the Color constructor, so that
+     * the alpha is not lost in postscript output.
+     *
+     * @param source
+     * @param alpha
+     * @return
+     */
+    public static Color getCompositeColor(float[] source, float alpha) {
+        return getCompositeColor(whiteComponents, source, alpha);
+    }
 }
diff --git a/src/org/broad/igv/util/CompressionUtils.java b/src/org/broad/igv/util/CompressionUtils.java
index 6195a29..30155fd 100644
--- a/src/org/broad/igv/util/CompressionUtils.java
+++ b/src/org/broad/igv/util/CompressionUtils.java
@@ -18,10 +18,7 @@
 
 package org.broad.igv.util;
 
-import org.apache.log4j.Logger;
-
 import java.io.ByteArrayOutputStream;
-import java.io.IOException;
 import java.util.zip.Deflater;
 import java.util.zip.Inflater;
 
@@ -34,8 +31,6 @@ import java.util.zip.Inflater;
  */
 public class CompressionUtils {
 
-    private static Logger log = Logger.getLogger(CompressionUtils.class);
-
 
     public static byte[] decompress(byte[] data) {
         return decompress(data, data.length * 4);
@@ -78,11 +73,7 @@ public class CompressionUtils {
             }
         }
 
-        try {
-            bos.close();
-        } catch (IOException e) {
-            log.error("Error closing stream", e);
-        }
+        StreamUtils.closeQuitely(bos);
 
         // Return the decompressed data
         return bos.toByteArray();
@@ -109,11 +100,7 @@ public class CompressionUtils {
             int count = compressor.deflate(buf);
             bos.write(buf, 0, count);
         }
-        try {
-            bos.close();
-        } catch (IOException e) {
-            log.error("Error closing stream", e);
-        }
+        StreamUtils.closeQuitely(bos);
 
 
         byte[] compressedData = bos.toByteArray();
diff --git a/src/org/broad/igv/util/CompressionUtilsTest.java b/src/org/broad/igv/util/CompressionUtilsTest.java
new file mode 100644
index 0000000..505d1bf
--- /dev/null
+++ b/src/org/broad/igv/util/CompressionUtilsTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.util;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/**
+ * User: jrobinso
+ * Date: Mar 20, 2010
+ */
+public class CompressionUtilsTest {
+
+
+    @Test
+    public void testCompression() {
+        int sz = 1000000;
+        byte[] uncompressedBytes = new byte[sz];
+        for (int i = 0; i < sz; i++) {
+            uncompressedBytes[i] = (byte) (Math.sin(i) * 100);
+        }
+
+        byte[] compressedBytes = CompressionUtils.compress(uncompressedBytes);
+        byte[] result = CompressionUtils.decompress(compressedBytes);
+
+        assertEquals(uncompressedBytes.length, result.length);
+        for (int i = 0; i < result.length; i++) {
+            assertEquals(uncompressedBytes[i], result[i]);
+        }
+    }
+
+    @Test
+    public void testCompressionChunked() {
+        int sz = 1000000;
+        byte[] uncompressedBytes = new byte[sz];
+        for (int i = 0; i < sz; i++) {
+            uncompressedBytes[i] = (byte) (Math.sin(i) * 100);
+        }
+
+        // Compress the data in 32k chunks
+        int chunkSize = 32000;
+        byte[] compressedBytes = CompressionUtils.compress(uncompressedBytes, chunkSize);
+
+        // Decompress.  Pass an incorrect chunk size (too small), it should still decompress correctly
+        byte[] result = CompressionUtils.decompress(compressedBytes, chunkSize - 1000);
+        assertEquals(uncompressedBytes.length, result.length);
+        for (int i = 0; i < result.length; i++) {
+            assertEquals(uncompressedBytes[i], result[i]);
+        }
+
+        // Decompress.  Pass an incorrect chunk size (too large), it should still decompress correctly
+        result = CompressionUtils.decompress(compressedBytes, chunkSize + 1000);
+        assertEquals(uncompressedBytes.length, result.length);
+        for (int i = 0; i < result.length; i++) {
+            assertEquals(uncompressedBytes[i], result[i]);
+        }
+    }
+
+
+}
diff --git a/src/org/broad/igv/util/ConnectionTimerTask.java b/src/org/broad/igv/util/ConnectionTimerTask.java
index 267f01c..b73955e 100644
--- a/src/org/broad/igv/util/ConnectionTimerTask.java
+++ b/src/org/broad/igv/util/ConnectionTimerTask.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/ConvertPlasmodium.java b/src/org/broad/igv/util/ConvertPlasmodium.java
index 6969625..dba4c87 100644
--- a/src/org/broad/igv/util/ConvertPlasmodium.java
+++ b/src/org/broad/igv/util/ConvertPlasmodium.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/EpigeneticsUtils.java b/src/org/broad/igv/util/EpigeneticsUtils.java
new file mode 100644
index 0000000..c0784cb
--- /dev/null
+++ b/src/org/broad/igv/util/EpigeneticsUtils.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.util;
+
+import org.broad.igv.track.TrackType;
+
+import java.awt.*;
+import java.io.*;
+import java.util.*;
+import java.util.List;
+
+/**
+ * Utility methods to deal with Bernstein's groups file conventions.
+ */
+
+public class EpigeneticsUtils {
+
+
+    static int step = 25;
+    static int span = 25;
+
+    public static void main(String[] args) {
+        try {
+            String dir = "/Volumes/igv/data/public/epigenetics/WilmsTumor/wig/orig";
+            String outputDir = "/Volumes/igv/data/public/epigenetics/WilmsTumor/wig";
+            //convertToTDF(new File(dir), outputDir);
+            createCombinedWigs(new File(dir), new File(outputDir));
+        } catch (Exception e) {
+            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+        }
+    }
+
+    public static void convertToTDF(File inputDir, String outputDir) {
+        for (File f : inputDir.listFiles()) {
+            String ifile = f.getName();
+            if (ifile.endsWith(".wig")) {
+                String oFile = (outputDir + f.getName() + ".tdf");
+                String logFile = f.getName() + ".out";
+                System.out.println("/xchip/igv/tools/igvtools tile " + ifile + " " + oFile + " hg18 > " + logFile + " &");
+            }
+        }
+    }
+
+
+
+    public static void createCombinedWigs(File inputDir, File outputDir) throws IOException {
+
+
+        Map<String, List<File>> fileMap = getFilesBySample(inputDir);
+
+        for (Map.Entry<String, List<File>> entry : fileMap.entrySet()) {
+
+            BufferedReader br;
+            PrintWriter pw;
+
+            String sampleName = entry.getKey();
+            File oFile = new File(outputDir, sampleName + ".wig");
+
+            pw = new PrintWriter(new BufferedWriter(new FileWriter(oFile)));
+
+            String cs = getColor(sampleName);
+            pw.println("#type=" + TrackType.CHIP);
+            pw.print("track  type=wiggle_0 name=" + sampleName + " viewLimits=0:10");
+            if (cs == null) {
+                pw.println();
+            } else {
+                pw.println(" color=" + cs);
+            }
+
+            for (File f : entry.getValue()) {
+
+                br = ParsingUtils.openBufferedReader(f.getAbsolutePath());
+                String stepLine = br.readLine();
+                if (!stepLine.startsWith("fixedStep") || !stepLine.contains("start=1")) {
+                    throw new RuntimeException("Unexpected step line: " + stepLine);
+                }
+
+                String[] tokens = stepLine.split(" ");
+                String chromString = null;
+                for (String s : tokens) {
+                    if (s.startsWith("chrom")) {
+                        chromString = s.trim();
+                        break;
+                    }
+                }
+                if (chromString == null) {
+                    throw new RuntimeException("Missing chrom string: " + stepLine);
+                }
+                String chr = chromString.split("=")[1];
+
+
+
+
+
+                // First line
+                String nextLine = br.readLine();
+                int value = Integer.parseInt(nextLine.trim());
+                int startPos = 0;
+                int endPos = step;
+
+                while ((nextLine = br.readLine()) != null) {
+
+                    int v = Integer.parseInt(nextLine.trim());
+
+                    if (v != value && value >= 0) {
+                        pw.println(chr + "\t" + startPos + "\t" + endPos + "\t" + value);
+                        startPos = endPos;
+
+                    }
+
+                    if (value < 0) {
+                        startPos += step;
+                    }
+                    value = v;
+                    endPos += step;
+                }
+
+                // Write the last interval
+                if (value >= 0) {
+                    pw.println(chr + "\t" + startPos + "\t" + endPos + "\t" + value);
+                    startPos = endPos;
+
+                }
+
+
+                br.close();
+
+                /*
+                pw.println("variableStep " + chromString + " span=" + span);
+                int pos = 1;
+                String nextLine;
+                while ((nextLine = br.readLine()) != null) {
+                    int value = Integer.parseInt(nextLine.trim());
+                    if (value > 0) {
+                        pw.println(pos + "\t" + value);
+                    }
+                    pos += step;
+                }
+                br.close();
+                */
+            }
+
+
+            pw.close();
+
+
+        }
+
+    }
+
+
+    /**
+     * @param directory
+     * @return
+     */
+    static Map<String, List<File>> getFilesBySample(File directory) {
+
+        Map<String, List<File>> fileMap = new HashMap();
+
+        for (File f : directory.listFiles()) {
+
+            String fn = f.getName();
+            if (fn.endsWith("wig") || fn.endsWith("wig.gz")) {
+                String[] tokens = fn.split("_");
+                if (tokens.length > 0) {
+                    int sz = tokens.length - 1;
+                    String s = tokens[0];
+                    for (int i = 1; i < sz; i++) {
+                        s += "_" + tokens[i];
+                    }
+
+                    List<File> files = fileMap.get(s);
+                    if (files == null) {
+                        files = new ArrayList();
+                        fileMap.put(s, files);
+                    }
+                    files.add(f);
+
+                }
+            }
+
+        }
+
+        return fileMap;
+    }
+
+
+    static String getColor(String sampleName) {
+
+        if (sampleName.contains("K4")) {
+            return "0,150,0";
+        } else if (sampleName.contains("K9")) {
+            return "100,0,0";
+        } else if (sampleName.contains("K27")) {
+            return "255,0,0";
+        }
+        return null;
+
+
+    }
+
+
+}
diff --git a/src/org/broad/igv/util/FileUtils.java b/src/org/broad/igv/util/FileUtils.java
index 619d10c..6f800fa 100644
--- a/src/org/broad/igv/util/FileUtils.java
+++ b/src/org/broad/igv/util/FileUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -21,24 +21,40 @@ import org.apache.log4j.Level;
 import org.apache.log4j.Logger;
 import org.apache.log4j.PatternLayout;
 import org.apache.log4j.RollingFileAppender;
-import org.broad.igv.IGVConstants;
+import org.broad.igv.ui.UIConstants;
+import org.broad.igv.ui.util.MessageUtils;
+import org.broad.tribble.util.HttpUtils;
 
 import java.io.*;
-import java.util.Map;
+import java.net.MalformedURLException;
+import java.net.URL;
 import java.util.HashMap;
+import java.util.Map;
 
 /**
  * @author jrobinso
  */
 public class FileUtils {
-    final private static String separator =
-            System.getProperties().getProperty("file.separator");
+    final private static String separator = System.getProperties().getProperty("file.separator");
     public static StringBuffer buffer = new StringBuffer();
     static private RollingFileAppender appender;
+    private static Logger log = Logger.getLogger(FileUtils.class);
+
+
+    public static boolean resourceExists(String path) {
+        try {
+            boolean remoteFile = isRemote(path);
+            return (!remoteFile && (new File(path).exists())) ||
+                    (remoteFile && HttpUtils.resourceAvailable(new URL(path)));
+        } catch (MalformedURLException e) {
+            log.error("Malformed URL: " + path, e);
+            return false;
+        }
+    }
+
 
-    public static void main(String[] args) {
-        fixEOL("/Users/jrobinso/Downloads/S.purpuratus/2.1-glean.gff3",
-                "/Users/jrobinso/Downloads/S.purpuratus/tmp.gff3");
+    public static boolean isRemote(String path) {
+        return path.startsWith("http://") || path.startsWith("https://") || path.startsWith("ftp://");
     }
 
     public static boolean canWriteTo(File file) {
@@ -215,6 +231,7 @@ public class FileUtils {
         return dir.delete();
     }
 
+
     public static void fixEOL(String ifile, String ofile) {
         BufferedReader br = null;
         PrintWriter pw = null;
@@ -274,7 +291,7 @@ public class FileUtils {
 
         // Build the log file path
         StringBuffer logFilePath = new StringBuffer();
-        logFilePath.append(IGVConstants.DEFAULT_IGV_DIRECTORY);
+        logFilePath.append(UIConstants.getIgvDirectory());
         logFilePath.append(separator);
         logFilePath.append("igv.log");
 
@@ -304,7 +321,7 @@ public class FileUtils {
 
         try {
             is = new FileInputStream(from);
-            int bytesCopied = createFileFromStream(new BufferedInputStream(is), to);
+            createFileFromStream(is, to);
 
         }
         finally {
@@ -317,32 +334,62 @@ public class FileUtils {
     /**
      * Create a file from an input stream.
      *
-     * @param is
+     * @param in
      * @param outputFile
      * @throws java.io.IOException
      */
-    public static int createFileFromStream(InputStream is, File outputFile) throws IOException {
+    public static void createFileFromStream(InputStream in, File outputFile) throws IOException {
 
         int totalSize = 0;
 
-        int chunkSize = 64000;
-        byte[] data = new byte[chunkSize];
-
-        FileOutputStream fileOutputStream = null;
+        FileOutputStream out = null;
 
         try {
-            fileOutputStream = new FileOutputStream(outputFile);
-            int bytesRead = 0;
-            while ((bytesRead = is.read(data)) > 0) {
-                totalSize += bytesRead;
-                fileOutputStream.write(data, 0, bytesRead);
+            out = new FileOutputStream(outputFile);
+            byte[] buffer = new byte[64000];
+            int bytes_read;
+            while ((bytes_read = in.read(buffer)) != -1)
+                out.write(buffer, 0, bytes_read);
+        }
+
+        catch (Exception e) {
+            outputFile.delete();
+            MessageUtils.showMessage("<html>Error downloading file: " + outputFile.getAbsoluteFile() +
+                    "<br/>" + e.toString());
+
+        }
+        finally {
+            if (out != null) {
+                out.flush();
+                out.close();
             }
-        } finally {
-            if (fileOutputStream != null) {
-                fileOutputStream.close();
+        }
+    }
+
+
+
+    public static void replaceStrings(File inputFile, File outputFile, Map<String, String> replace) throws IOException {
+
+        BufferedReader reader = null;
+        PrintWriter writer = null;
+
+        try {
+            reader = new BufferedReader(new FileReader(inputFile));
+            writer = new PrintWriter(new BufferedWriter(new FileWriter(outputFile)));
+            String nextLine;
+            while ((nextLine = reader.readLine()) != null) {
+                for (Map.Entry<String, String> entry : replace.entrySet()) {
+                    nextLine = nextLine.replace(entry.getKey(), entry.getValue());
+                }
+                writer.println(nextLine);
             }
+
+        }
+        finally {
+            reader.close();
+            writer.close();
+
         }
-        return totalSize;
     }
 
 
@@ -372,84 +419,7 @@ public class FileUtils {
         }
         return string;
     }
-    
-
-
-    /*
-    static public void main(String arg[]) {
-
-        File baseFile = new File("C:\\");
-        File targetFile = new File("C:\\");
-        String path = FileUtils.getRelativePath(baseFile, targetFile);
-        System.out.println("\nBase Path: " + baseFile.getAbsolutePath());
-        System.out.println("Target Path: " + targetFile.getAbsolutePath());
-        System.out.println("\tCalculated new target Path: " + path);
-        System.out.println("\tExpected new target Path: " + ".\\");
-
-        baseFile = new File("C:\\");
-        targetFile = new File("C:\\bdata\\foo\\last");
-        path = FileUtils.getRelativePath(baseFile, targetFile);
-        System.out.println("\nBase Path: " + baseFile.getAbsolutePath());
-        System.out.println("Target Path: " + targetFile.getAbsolutePath());
-        System.out.println("\tCalculated new target Path: " + path);
-        System.out.println("\tExpected new target Path: " + ".\\bdata\\foo\\last");
-
-        baseFile = new File("C:\\BaseDirectory\\base");
-        targetFile = new File("C:\\");
-        path = FileUtils.getRelativePath(baseFile, targetFile);
-        System.out.println("\nBase Path: " + baseFile.getAbsolutePath());
-        System.out.println("Target Path: " + targetFile.getAbsolutePath());
-        System.out.println("\tCalculated new target Path: " + path);
-        System.out.println("\tExpected new target Path: " + "..\\..\\");
-
-        baseFile = new File("C:\\BaseDirectory\\base");
-        targetFile = new File("C:\\BaseDirectory\\base");
-        path = FileUtils.getRelativePath(baseFile, targetFile);
-        System.out.println("\nBase Path: " + baseFile.getAbsolutePath());
-        System.out.println("Target Path: " + targetFile.getAbsolutePath());
-        System.out.println("\tCalculated new target Path: " + path);
-        System.out.println("\tExpected new target Path: " + ".\\");
-
-        baseFile = new File("C:\\BaseDirectory\\base\\node\\last");
-        targetFile = new File("C:\\bdata\\foo\\last");
-        path = FileUtils.getRelativePath(baseFile, targetFile);
-        System.out.println("\nBase Path: " + baseFile.getAbsolutePath());
-        System.out.println("Target Path: " + targetFile.getAbsolutePath());
-        System.out.println("\tCalculated new target Path: " + path);
-        System.out.println("\tExpected new target Path: " + "..\\..\\..\\..\\bdata\\foo\\last");
-
-        baseFile = new File("C:\\BaseDirectory\\base");
-        targetFile = new File("C:\\bdata\\foo\\last");
-        path = FileUtils.getRelativePath(baseFile, targetFile);
-        System.out.println("\nBase Path: " + baseFile.getAbsolutePath());
-        System.out.println("Target Path: " + targetFile.getAbsolutePath());
-        System.out.println("\tCalculated new target Path: " + path);
-        System.out.println("\tExpected new target Path: " + "..\\..\\bdata\\foo\\last");
-
-        baseFile = new File("C:\\BaseDirectory\\base");
-        targetFile = new File("C:\\BaseDirectory\\base\\node\\last");
-        path = FileUtils.getRelativePath(baseFile, targetFile);
-        System.out.println("\nBase Path: " + baseFile.getAbsolutePath());
-        System.out.println("Target Path: " + targetFile.getAbsolutePath());
-        System.out.println("\tCalculated new target Path: " + path);
-        System.out.println("\tExpected new target Path: " + ".\\node\\last");
-
-        baseFile = new File("C:\\BaseDirectory\\base\\node\\last");
-        targetFile = new File("C:\\bdata\\foo\\last\\test_file.txt");
-        path = FileUtils.getRelativePath(baseFile, targetFile);
-        System.out.println("\nBase Path: " + baseFile.getAbsolutePath());
-        System.out.println("Target Path: " + targetFile.getAbsolutePath());
-        System.out.println("\tCalculated new target Path: " + path);
-        System.out.println("\tExpected new target Path: " + "..\\..\\..\\..\\bdata\\foo\\last\\test_file.txt");
-
-        baseFile = new File("C:\\bdata\\foo\\last");
-        targetFile = new File("C:\\bdata\\foo\\last\\test_file.txt");
-        path = FileUtils.getRelativePath(baseFile, targetFile);
-        System.out.println("\nBase Path: " + baseFile.getAbsolutePath());
-        System.out.println("Target Path: " + targetFile.getAbsolutePath());
-        System.out.println("\tCalculated new target Path: " + path);
-        System.out.println("\tExpected new target Path: " + ".\\test_file.txt");
-    }
-    */
+
+
 }
 
diff --git a/src/org/broad/igv/util/Filter.java b/src/org/broad/igv/util/Filter.java
index acbab58..68b2154 100644
--- a/src/org/broad/igv/util/Filter.java
+++ b/src/org/broad/igv/util/Filter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/FilterElement.java b/src/org/broad/igv/util/FilterElement.java
index 5332437..fb4a1a1 100644
--- a/src/org/broad/igv/util/FilterElement.java
+++ b/src/org/broad/igv/util/FilterElement.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -180,7 +180,7 @@ abstract public class FilterElement {
         } else {
 
             // Compare as strings
-            comparison = ((String) comparableItem).compareTo(expectedValue);
+            comparison = (comparableItem).compareTo(expectedValue);
         }
 
         if (comparisonOperator.equals(Operator.EQUAL)) {
@@ -299,7 +299,6 @@ abstract public class FilterElement {
     }
 
     /**
-     * @param data           track to be evaluated
      * @param previousResult Result of the previous FilterElement that is
      *                       being chained to this one (null if no previous FilterElement);
      * @return
diff --git a/src/org/broad/igv/util/GapToWigConverter.java b/src/org/broad/igv/util/GapToWigConverter.java
index 890e1c4..51b3a69 100644
--- a/src/org/broad/igv/util/GapToWigConverter.java
+++ b/src/org/broad/igv/util/GapToWigConverter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/HttpUtils.java b/src/org/broad/igv/util/HttpUtils.java
deleted file mode 100644
index 97f6bf3..0000000
--- a/src/org/broad/igv/util/HttpUtils.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-package org.broad.igv.util;
-
-import org.apache.log4j.Logger;
-
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLConnection;
-import java.io.InputStream;
-import java.io.IOException;
-
-/**
- * Created by IntelliJ IDEA.
- * User: jrobinso
- * Date: Sep 23, 2009
- * Time: 5:51:21 PM
- * To change this template use File | Settings | File Templates.
- */
-public class HttpUtils {
-
-    private static Logger log = Logger.getLogger(HttpUtils.class);
-
-    public static String getETag(URL url) {
-        URLConnection conn = null;
-        try {
-            // Create a URLConnection object for a URL
-            conn = url.openConnection();
-            conn.setReadTimeout(3000);
-            return conn.getHeaderField("ETag");
-        } catch (Exception e) {
-            e.printStackTrace();
-            return null;
-        }
-        finally {
-            if (conn != null && conn instanceof HttpURLConnection) {
-                ((HttpURLConnection) conn).disconnect();
-            }
-        }
-    }
-
-    public static String getHeaderField(URL url, String name) {
-        URLConnection conn = null;
-        try {
-            // Create a URLConnection object for a URL
-            conn = url.openConnection();
-            conn.setReadTimeout(3000);
-            return conn.getHeaderField(name);
-
-        } catch (Exception e) {
-            log.error("Error getting header: " + name + " from URL: " + url);
-            return null;
-        }
-        finally {
-            if (conn != null && conn instanceof HttpURLConnection) {
-                ((HttpURLConnection) conn).disconnect();
-            }
-        }
-    }
-
-    public static void printHeaderFields(URL url) {
-
-        URLConnection conn = null;
-        try {
-            // Create a URLConnection object for a URL
-            conn = url.openConnection();
-            conn.setReadTimeout(3000);
-
-            for (String name : conn.getHeaderFields().keySet()) {
-                System.out.println(name + "\t" + conn.getHeaderField(name));
-
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        finally {
-            if (conn != null && conn instanceof HttpURLConnection) {
-                ((HttpURLConnection) conn).disconnect();
-            }
-        }
-    }
-
-    public static boolean resourceAvailable(URL url) {
-        InputStream is = null;
-        try {
-            is = url.openStream();
-            is.read();
-            return true;
-
-        } catch (Exception e) {
-            log.error(e);
-            return false;
-        }
-        finally {
-            if (is != null) {
-                try {
-                    is.close();
-                } catch (IOException e) {
-                    log.error("", e);
-                }
-            }
-        }
-
-    }
-
-    public static void main(String[] args) throws MalformedURLException {
-        //printHeaderFields(new URL(
-        //        "http://www.broadinstitute.org/igvdata/1KG/DCC_merged/freeze5/NA12891.pilot2.SLX.bam"));
-        System.out.println(getETag(new URL(
-                "http://www.broadinstitute.org/igvdata/test/sam/303KY.8.paired1.bam.tdf")));
-        System.out.println(resourceAvailable(new URL(
-                "http://www.broadinstitute.org/igvdata/test/sam/303KY.8.paired1.bam.tdf")));
-
-
-    }
-}
diff --git a/src/org/broad/igv/util/IGVHttpUtils.java b/src/org/broad/igv/util/IGVHttpUtils.java
new file mode 100644
index 0000000..6d89060
--- /dev/null
+++ b/src/org/broad/igv/util/IGVHttpUtils.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.util;
+
+import org.apache.log4j.Logger;
+import org.apache.commons.net.ftp.FTPClient;
+import org.apache.commons.net.ftp.FTPReply;
+import org.broad.igv.PreferenceManager;
+import org.broad.igv.exceptions.DataLoadException;
+import org.broad.igv.ui.IGVMainFrame;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.*;
+import java.util.HashMap;
+import java.util.Map;
+
+public class IGVHttpUtils {
+
+    private static Logger log = Logger.getLogger(IGVHttpUtils.class);
+
+    /**
+     * Proxy settings (can be null)
+     */
+    private static ProxySettings proxySettings = null;
+    static boolean byteRangeTested = false;
+    static boolean useByteRange = true;
+
+    private static boolean testByteRange() {
+
+        try {
+            String testURL = "http://www.broadinstitute.org/igvdata/byteRangeTest.txt";
+            byte[] expectedBytes = {(byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o'};
+
+            SeekableHTTPStream str = new SeekableHTTPStream(new URL(testURL));
+            str.seek(10);
+            byte[] buffer = new byte[5];
+            str.read(buffer, 0, 5, true);
+
+            for (int i = 0; i < buffer.length; i++) {
+                if (buffer[i] != expectedBytes[i]) {
+                    return false;
+                }
+            }
+            return true;
+        } catch (IOException e) {
+            SeekableHTTPStream.log.error("Error while testing byte range ", e);
+            return false;
+        }
+    }
+
+    public static synchronized boolean useByteRange() {
+        useByteRange = PreferenceManager.getInstance().isUseByteRange();
+        if (useByteRange && !byteRangeTested) {
+            useByteRange = testByteRange();
+            byteRangeTested = true;
+        }
+        return useByteRange;
+    }
+
+
+    public static class ProxySettings {
+        boolean auth = false;
+        String user;
+        String pw;
+        boolean useProxy;
+        String proxyHost;
+        int proxyPort = -1;
+
+        public ProxySettings(boolean useProxy, String user, String pw, boolean auth, String proxyHost, int proxyPort) {
+            this.auth = auth;
+            this.proxyHost = proxyHost;
+            this.proxyPort = proxyPort;
+            this.pw = pw;
+            this.useProxy = useProxy;
+            this.user = user;
+        }
+    }
+
+
+    /**
+     * Wraps url.openConnection(),  and adds proxy authentication if required.
+     *
+     * @param url
+     * @return
+     * @throws java.io.IOException
+     */
+
+
+    public static InputStream openConnectionStream(URL url) throws IOException {
+        if (url.getProtocol().toUpperCase().equals("FTP")) {
+            return openFtpStream(url);
+        } else {
+            return openHttpStream(url, (Map<String, String>) null);
+        }
+    }
+
+
+    public static InputStream openHttpStream(URL url, Map<String, String> requestProperties) throws IOException {
+
+        HttpURLConnection conn = openConnectionPrivate(url, requestProperties);
+        return openHttpStream(url, conn);
+
+
+    }
+
+    public static InputStream openHttpStream(URL url, HttpURLConnection conn) throws IOException {
+        // IF this is a protected directory we will get a 401.  Continue requesting a user / password until
+        // the user cancels or the connectino succeeds.
+
+        while (true) {
+            InputStream is = null;
+            try {
+                is = conn.getInputStream();
+                return is;
+            }
+            catch (SocketTimeoutException e) {
+                throw e;
+            }
+            catch (IOException e) {
+                if (conn.getResponseCode() == 401) {
+                    if (is != null) {
+                        is.close();
+                    }
+                    conn.disconnect();
+
+                    if (getUserPass(url.toExternalForm()) == false) {
+                        throw e;
+                    } else {
+                        Map<String, String> requestProperties = new HashMap();
+                        for (Map.Entry<String, java.util.List<String>> entry : conn.getRequestProperties().entrySet()) {
+                            if (entry.getValue().size() > 0) {
+                                requestProperties.put(entry.getKey(), entry.getValue().get(0));
+                            }
+                        }
+                        conn.getRequestProperties();
+                        conn = openConnectionPrivate(url, null);
+                    }
+
+                } else {
+                    throw e;
+                }
+            }
+
+        }
+    }
+
+    private static InputStream openFtpStream(URL url) throws IOException {
+
+        String host = url.getHost();
+
+        FTPClient ftp = new FTPClient();
+        ftp.connect(host);
+        System.out.println(ftp.getReplyString());
+
+        // After connection attempt, you should check the reply code to verify
+        // success.
+        int reply = ftp.getReplyCode();
+
+        if (!FTPReply.isPositiveCompletion(reply)) {
+            ftp.disconnect();
+            System.err.println("FTP server refused connection.");
+            throw new RuntimeException("FTP server refused connection.");
+        }
+
+        boolean success = ftp.login("anonymous", "igv-team at broadinstitute.org");
+        if (!success) {
+            System.err.println("FTP login failed " + ftp.getReplyString());
+            throw new RuntimeException("FTP login failed " + ftp.getReplyString());
+        }
+
+        // Use passive mode as default because most of us are
+        // behind firewalls these days.
+        ftp.enterLocalPassiveMode();
+
+
+        String file = url.getPath();
+        System.out.println("Open file: " + file);
+        return ftp.retrieveFileStream(file);
+    }
+
+
+    private static HttpURLConnection openConnectionPrivate(URL url, Map<String, String> requestProperties) throws IOException {
+        HttpURLConnection conn = openConnection(url);
+        conn.setConnectTimeout(10000);
+        conn.setReadTimeout(60000);
+        conn.setRequestMethod("GET");
+        conn.setRequestProperty("Connection", "close");
+        if (requestProperties != null) {
+            for (Map.Entry<String, String> prop : requestProperties.entrySet()) {
+                conn.setRequestProperty(prop.getKey(), prop.getValue());
+            }
+        }
+        return conn;
+    }
+
+
+    public static HttpURLConnection openConnection(URL url) throws IOException {
+        if (useProxy()) {
+            Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxySettings.proxyHost, proxySettings.proxyPort));
+            HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy);
+            if (proxySettings.auth && proxySettings.user != null && proxySettings.pw != null) {
+                String encodedUserPwd = base64Encode(proxySettings.user + ":" + proxySettings.pw);
+                conn.setRequestProperty("Proxy-Authorization", "Basic " + encodedUserPwd);
+            }
+            return conn;
+        } else {
+            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+            return conn;
+
+        }
+    }
+
+    private static boolean useProxy() {
+        return proxySettings != null && proxySettings.useProxy && proxySettings.proxyHost != null && proxySettings.proxyPort > 0;
+    }
+
+
+    /**
+     * Todo,  add comment -- what is returned
+     *
+     * @param locationString
+     * @return
+     */
+    public static boolean getUserPass(String locationString) {
+
+        //http://www.broadinstitute.org/igvdata/private/cpgIslands.hg18.bed
+        JPanel passPanel = new JPanel();
+        passPanel.setLayout(new GridLayout(6, 1));
+
+        JLabel message = new JLabel("Please enter your Username and Password");
+        JLabel location = new JLabel(locationString);
+        JLabel username = new JLabel("User:");
+        JLabel password = new JLabel("Pass:");
+        JTextField userField = new JTextField();
+        JPasswordField passwordField = new JPasswordField();
+        passPanel.add(message);
+        passPanel.add(location);
+        passPanel.add(username);
+        passPanel.add(userField);
+        passPanel.add(password);
+        passPanel.add(passwordField);
+
+        int a = JOptionPane.showConfirmDialog(IGVMainFrame.getInstance(), passPanel, "Authentication Required", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
+
+        if (a == JOptionPane.CANCEL_OPTION) {
+            return false;
+        } else {
+            final String userString = userField.getText();
+            final char[] userPass = passwordField.getPassword();
+            Authenticator.setDefault(new Authenticator() {
+                protected PasswordAuthentication getPasswordAuthentication() {
+                    return new PasswordAuthentication(userString, userPass);
+                }
+            });
+            return true;
+        }
+    }
+
+    public static void updateProxySettings() {
+
+
+        boolean useProxy;
+        String proxyHost;
+        int proxyPort = -1;
+        boolean auth = false;
+        String user = null;
+        String pw = null;
+
+        PreferenceManager prefMgr = PreferenceManager.getInstance();
+        useProxy = prefMgr.getBooleanPreference(PreferenceManager.USE_PROXY, false);
+        proxyHost = prefMgr.get(PreferenceManager.PROXY_HOST, null);
+        try {
+            proxyPort = Integer.parseInt(prefMgr.get(PreferenceManager.PROXY_PORT, "-1"));
+        } catch (NumberFormatException e) {
+            proxyPort = -1;
+        }
+        auth = prefMgr.getBooleanPreference(PreferenceManager.PROXY_AUTHENTICATE, false);
+        user = prefMgr.get(PreferenceManager.PROXY_USER, null);
+        String pwString = prefMgr.get(PreferenceManager.PROXY_PW, null);
+        if (pwString != null) {
+            pw = Utilities.base64Decode(pwString);
+        }
+
+
+        ProxySettings proxySettings = new ProxySettings(useProxy, user, pw, auth, proxyHost, proxyPort);
+        setProxySettings(proxySettings);
+    }
+
+
+    // TODO -- replace use of sun package
+
+    public static String base64Encode(String str) {
+        sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
+        byte[] bytes = str.getBytes();
+        return encoder.encode(bytes);
+
+    }
+
+    // TODO -- replace use of sun package
+
+    public static String base64Decode(String str) {
+        try {
+            return new String((new sun.misc.BASE64Decoder()).decodeBuffer(str));
+        } catch (IOException e) {
+            log.error("Error decoding string: " + str, e);
+            return str;
+        }
+    }
+
+
+    public static void setProxySettings(ProxySettings ps) {
+        proxySettings = ps;
+    }
+
+    public static String getETag(URL url) {
+        return getHeaderField(url, "ETag");
+    }
+
+    public static String getHeaderField(URL url, String name) {
+
+        URLConnection conn = null;
+        try {
+            // Create a URLConnection object for a URL
+            conn = openConnection(url);
+            conn.setReadTimeout(5000);
+            ((HttpURLConnection) conn).setRequestMethod("HEAD"); 
+            return conn.getHeaderField(name);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+        finally {
+            if (conn != null && conn instanceof HttpURLConnection) {
+                ((HttpURLConnection) conn).disconnect();
+            }
+        }
+    }
+
+    public static boolean resourceAvailable(URL url) {
+        URLConnection conn = null;
+        try {
+            // Create a URLConnection object for a URL
+            conn = openConnection(url);
+            ((HttpURLConnection) conn).setRequestMethod("HEAD");
+            conn.setReadTimeout(5000);
+            return conn.getHeaderField("ETag") != null;
+        } catch (Exception e) {
+            return false;
+        }
+        finally {
+            if (conn != null && conn instanceof HttpURLConnection) {
+                ((HttpURLConnection) conn).disconnect();
+            }
+        }
+    }
+
+}
diff --git a/src/org/broad/igv/util/IlluminaUtils.java b/src/org/broad/igv/util/IlluminaUtils.java
index 11b530d..86f28b3 100644
--- a/src/org/broad/igv/util/IlluminaUtils.java
+++ b/src/org/broad/igv/util/IlluminaUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/IntComparator.java b/src/org/broad/igv/util/IntComparator.java
index 33769fa..aca04be 100644
--- a/src/org/broad/igv/util/IntComparator.java
+++ b/src/org/broad/igv/util/IntComparator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/IntHashMap.java b/src/org/broad/igv/util/IntHashMap.java
index 590bf6c..77ef54e 100644
--- a/src/org/broad/igv/util/IntHashMap.java
+++ b/src/org/broad/igv/util/IntHashMap.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/JavaLibTests.java b/src/org/broad/igv/util/JavaLibTests.java
index f6a51ce..7752897 100644
--- a/src/org/broad/igv/util/JavaLibTests.java
+++ b/src/org/broad/igv/util/JavaLibTests.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/LRUCache.java b/src/org/broad/igv/util/LRUCache.java
index 8f92bad..82a511b 100644
--- a/src/org/broad/igv/util/LRUCache.java
+++ b/src/org/broad/igv/util/LRUCache.java
@@ -1,50 +1,98 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
+ * Copyright (c) 2007-2011 by The Broad Institute, Inc. and the Massachusetts Institute of
+ * Technology.  All Rights Reserved.
  *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL),
+ * Version 2.1 which is available at http://www.opensource.org/licenses/lgpl-2.1.php.
  *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR
+ * WARRANTES OF ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING,
+ * WITHOUT LIMITATION, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, WHETHER
+ * OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR RESPECTIVE
+ * TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES
+ * OF ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
+ * ECONOMIC DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER
+ * THE BROAD OR MIT SHALL BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT
+ * SHALL KNOW OF THE POSSIBILITY OF THE FOREGOING.
  */
 package org.broad.igv.util;
 
 import org.apache.log4j.Logger;
 
-import java.util.LinkedHashMap;
-import java.util.Map;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+import java.util.*;
 
 /**
  * @author jrobinso
  */
-public class LRUCache<K, V> extends LinkedHashMap<K, V> {
+public class LRUCache<K, V> {
 
     Logger log = Logger.getLogger(LRUCache.class);
 
-    private int maxEntries = 100;
 
-    public LRUCache(int maxEntries) {
-        this.maxEntries = maxEntries;
+    private static Map<Object, LRUCache> instances = Collections.synchronizedMap(new WeakHashMap<Object, LRUCache>());
+
+    public static void clearCaches() {
+        for (LRUCache cache : instances.values()) {
+            if (cache != null) {
+                cache.clear();
+            }
+        }
+    }
+
+
+    private final int maxEntries;
+
+    private SoftReference<Map<K, V>> mapReference;
+
+
+    public LRUCache(Object source, int max) {
+        instances.put(source, this);
+        this.maxEntries = max;
+        createMap();
+    }
+
+    private void createMap() {
+        mapReference = new SoftReference(Collections.synchronizedMap(
+                new LinkedHashMap<K, V>() {
+                    @Override
+                    protected boolean removeEldestEntry(Map.Entry eldest) {
+                        return (size() > maxEntries);
+                    }
+                }));
     }
 
-    @Override
-    protected boolean removeEldestEntry(Map.Entry eldest) {
-        if (size() > maxEntries) {
-            return true;
-        } else if (RuntimeUtils.getAvailableMemoryFraction() < 0.3) {
-            log.info("Memory low.  Free cache entry");
-            return true;
-        } else {
-            return false;
+    private Map<K, V> getMap() {
+        if (mapReference == null || mapReference.get() == null) {
+            createMap();
         }
+        return mapReference.get();
+    }
+
+    public V put(K k, V v) {
+        return getMap().put(k, v);
     }
+
+    public V get(Object key) {
+        return getMap().get(key);
+    }
+
+    public boolean containsKey(Object o) {
+        return getMap().containsKey(o);
+    }
+
+    public boolean isEmpty() {
+        return getMap().isEmpty();    //To change body of overridden methods use File | Settings | File Templates.
+    }
+
+    public void clear() {
+        getMap().clear();
+    }
+
+
+
+
 }
 
diff --git a/src/org/broad/igv/util/LocalBinaryReader.java b/src/org/broad/igv/util/LocalBinaryReader.java
deleted file mode 100644
index 8e1ec47..0000000
--- a/src/org/broad/igv/util/LocalBinaryReader.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
- * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
- *
- * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
- * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
- * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
- * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
- * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
- * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
- * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
- * FOREGOING.
- */
-
-/*
- * LocalBinaryReader.java
- *
- * Created on August 21, 2007, 8:31 AM
- *
- * To change this template, choose Tools | Template Manager
- * and open the template in the editor.
- */
-
-package org.broad.igv.util;
-
-import java.io.*;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @author jrobinso
- */
-public class LocalBinaryReader extends BinaryReader {
-
-    /**
-     * Creates a new instance of LocalBinaryReader
-     */
-    public LocalBinaryReader() {
-    }
-
-    protected byte[] readBytes(File file) {
-        try {
-            int fileSize = (int) file.length();
-            BufferedInputStream inStream = new BufferedInputStream(new FileInputStream(file), fileSize);
-
-            byte[] bytes = super.readBytes(inStream, fileSize);
-            inStream.close();
-            return bytes;
-        } catch (IOException ex) {
-            ex.printStackTrace();
-            throw new RuntimeException(ex);
-        }
-
-    }
-
-
-    /**
-     * Read a selection of sampledBytes from the given file.
-     * start -  logical position in file to start reading.  The logical
-     * position is related to the file position as follows
-     * file position = logical position X  readSize.
-     * <p/>
-     * stop  -  logical position in file to stop (should be <= file length)
-     * <p/>
-     * readSize - number of sampledBytes for a single read (4 sampledBytes for floats)
-     * <p/>
-     * freq - how often to read.
-     * <p/>
-     * Example -- to read every 5th float enter a readSize=4,  freq=5
-     */
-    public byte[] sampleBytes(File inputFile, int start, int stop, int readSize, int freq) throws IOException {
-
-
-        // Read the entire region into a byte array.  In most cases this is faster than
-        // reading individual floats and skipping on the file stream.
-        InputStream is = new BufferedInputStream(new FileInputStream(inputFile));
-        byte[] allBytes = new byte[(stop - start) * readSize];
-        is.skip(start * readSize);
-        is.read(allBytes);
-        is.close();
-
-        // Compute the number of chunks to read, and the total number of bytes
-        // required.  Create an array to hold the results.
-        int numberOfReads = (int) ((stop - start) / freq);
-        int numberOfBytes = numberOfReads * readSize;
-        byte[] sampledBytes = new byte[numberOfBytes];
-
-        // Stream over the file contents buffer (allBytes) reading every "freq"
-        // chunk into the results buffer (sampledBytes)
-        ByteArrayInputStream dis = new ByteArrayInputStream(allBytes);
-        int numRead = 0;
-        int nSkip = (freq - 1) * readSize;
-        while (numRead < sampledBytes.length) {
-            numRead += dis.read(sampledBytes, numRead, readSize);
-            dis.skip(nSkip);
-        }
-        return sampledBytes;
-    }
-
-
-    /**
-     * Return all data files in the data directory. This includes location,
-     * marker (snp) id, and copy number files for all chromosomes.
-     */
-    public List<File> getDataFiles(String dataFileDirectory) {
-        File[] files = new File(dataFileDirectory).listFiles(new FilenameFilter() {
-            public boolean accept(File dir, String name) {
-                return name.endsWith("bin");
-            }
-        });
-        return (files == null ? new ArrayList<File>() : java.util.Arrays.asList(files));
-    }
-
-
-    public static void main(String[] args) {
-        String file = "data/solexa/chr6.MEF.K4.bin";
-        long t0 = System.currentTimeMillis();
-        BinaryReader reader = new LocalBinaryReader();
-        reader.setLittleEndian(true);
-        float[] floats = reader.readFloats(new File(file));
-        System.out.println(floats.length + " floats read in " + (System.currentTimeMillis() - t0) + " ms");
-    }
-
-
-}
diff --git a/src/org/broad/igv/util/LoggingUtils.java b/src/org/broad/igv/util/LoggingUtils.java
index ad76448..d1126fa 100644
--- a/src/org/broad/igv/util/LoggingUtils.java
+++ b/src/org/broad/igv/util/LoggingUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -32,7 +32,7 @@ public class LoggingUtils {
 
     public static void debug(String message, Logger logger) {
 
-        if(logger.isDebugEnabled()) {
+        if (logger.isDebugEnabled()) {
 
             message = message + "  " + Thread.currentThread().getName();
             logger.debug(message);
diff --git a/src/org/broad/igv/util/LongRunningTask.java b/src/org/broad/igv/util/LongRunningTask.java
new file mode 100644
index 0000000..288e8db
--- /dev/null
+++ b/src/org/broad/igv/util/LongRunningTask.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+package org.broad.igv.util;
+
+import org.apache.log4j.Logger;
+import org.broad.igv.Globals;
+import org.broad.igv.ui.WaitCursorManager;
+import org.broad.igv.ui.WaitCursorManager.CursorToken;
+
+import java.util.concurrent.*;
+
+/**
+ * @author jrobinso
+ */
+public class LongRunningTask implements Callable {
+
+    private static Logger log = Logger.getLogger(LongRunningTask.class);
+
+    private static ExecutorService threadExecutor = Executors.newFixedThreadPool(3);
+    private static ScheduledExecutorService schedule = Executors.newScheduledThreadPool(1);
+
+    NamedRunnable runnable;
+
+    public static Future submit(NamedRunnable runnable) {
+        if (Globals.batch) {
+            log.debug("Running: " + runnable.getName());
+            runnable.run();
+            log.debug("Finished : " + runnable.getName());
+            return null;
+        } else {
+            return threadExecutor.submit(new LongRunningTask(runnable));
+        }
+    }
+
+
+    /**
+     * Schedule a task for execution in the future.
+     *
+     * @param runnable
+     * @param time
+     * @return
+     */
+    public static Future schedule(NamedRunnable runnable, long time) {
+        return schedule.schedule(new LongRunningTask(runnable), time, TimeUnit.MILLISECONDS);
+    }
+
+    public LongRunningTask(NamedRunnable runnable) {
+        this.runnable = runnable;
+    }
+
+    public Object call() throws Exception {
+
+        CursorToken token = WaitCursorManager.showWaitCursor();
+        try {
+            long t0 = System.currentTimeMillis();
+            runnable.run();
+            if (log.isDebugEnabled()) {
+                long dt = System.currentTimeMillis() - t0;
+                log.debug(runnable.getName() + "  time= " + dt);
+            }
+            return "";
+        } finally {
+            WaitCursorManager.removeWaitCursor(token);
+        }
+
+    }
+}
+
diff --git a/src/org/broad/igv/util/MD5Checksum.java b/src/org/broad/igv/util/MD5Checksum.java
new file mode 100644
index 0000000..5269db8
--- /dev/null
+++ b/src/org/broad/igv/util/MD5Checksum.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.util;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jesse
+ * Date: Jan 19, 2010
+ * Time: 5:30:36 PM
+ * To change this template use File | Settings | File Templates.
+ */
+
+import org.broad.igv.exceptions.DataLoadException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.security.MessageDigest;
+
+public class MD5Checksum {
+
+    private static byte[] createChecksum(String filename) throws
+            Exception {
+        InputStream fis = new FileInputStream(filename);
+
+        byte[] buffer = new byte[1024];
+        MessageDigest complete = MessageDigest.getInstance("MD5");
+        int numRead;
+        do {
+            numRead = fis.read(buffer);
+            if (numRead > 0) {
+                complete.update(buffer, 0, numRead);
+            }
+        } while (numRead != -1);
+        fis.close();
+        return complete.digest();
+    }
+
+    public static String getMD5Checksum(String filename) throws Exception {
+        File fileName = new File(filename);
+
+        //TODO Make a better output file naming convention.
+        if (fileName.isFile()) {
+            byte[] b = createChecksum(filename);
+            String result = "";
+            for (int i = 0; i < b.length; i++) {
+                result +=
+                        Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1);
+            }
+            return result;
+        }
+        throw new DataLoadException("No file found", filename);
+    }
+}
diff --git a/src/org/broad/igv/util/MacroSnapshotAction.java b/src/org/broad/igv/util/MacroSnapshotAction.java
index 1e32059..fef4cfd 100644
--- a/src/org/broad/igv/util/MacroSnapshotAction.java
+++ b/src/org/broad/igv/util/MacroSnapshotAction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,8 +23,9 @@ package org.broad.igv.util;
 
 //~--- non-JDK imports --------------------------------------------------------
 
+import org.apache.log4j.Logger;
+import org.broad.igv.session.ViewContext;
 import org.broad.igv.ui.IGVMainFrame;
-import org.broad.igv.ui.IGVModel;
 
 import java.io.File;
 import java.util.List;
@@ -35,6 +36,7 @@ import java.util.List;
 public class MacroSnapshotAction {
 
     public static File OUTPUT_DIRECTORY = new File(".");
+    private static Logger log = Logger.getLogger(MacroSnapshotAction.class);
 
     /**
      * Loop through a list of loci creating a screenshot for each.  Method
@@ -57,7 +59,7 @@ public class MacroSnapshotAction {
     final public static synchronized void setOutputDirectory(String dir) {
         OUTPUT_DIRECTORY = new File(dir);
         if (!OUTPUT_DIRECTORY.exists()) {
-            System.out.println("Warning: non existent directory: " + dir);
+            log.error("Warning: non existent directory: " + dir);
         }
     }
 
@@ -70,7 +72,7 @@ public class MacroSnapshotAction {
         IGVMainFrame mainFrame = IGVMainFrame.getInstance();
 
         if (filename == null) {
-            String locus = IGVModel.getInstance().getViewContext().getCurrentLocusString();
+            String locus = ViewContext.getInstance().getFormattedLocusString();
             filename = locus.replaceAll(":", "_").replace("-", "_") + ".png";
         }
 
@@ -80,7 +82,7 @@ public class MacroSnapshotAction {
 
 
         File file = new File(outputDirectory, filename);
-        System.out.println("Snapshot: " + filename);
+        log.info("Snapshot: " + filename);
         mainFrame.createSnapshotNonInteractive(file);
     }
 }
diff --git a/src/org/broad/igv/util/MiscTests.java b/src/org/broad/igv/util/MiscTests.java
index 7926000..40c677f 100644
--- a/src/org/broad/igv/util/MiscTests.java
+++ b/src/org/broad/igv/util/MiscTests.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/NamedRunnable.java b/src/org/broad/igv/util/NamedRunnable.java
new file mode 100644
index 0000000..1879149
--- /dev/null
+++ b/src/org/broad/igv/util/NamedRunnable.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.util;
+
+/**
+ * User: jrobinso
+ * Date: Feb 1, 2010
+ */
+public interface NamedRunnable extends Runnable {
+
+    public String getName();
+}
diff --git a/src/org/broad/igv/util/ObjectCache.java b/src/org/broad/igv/util/ObjectCache.java
index 4449dbe..212064c 100644
--- a/src/org/broad/igv/util/ObjectCache.java
+++ b/src/org/broad/igv/util/ObjectCache.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/ParsingUtils.java b/src/org/broad/igv/util/ParsingUtils.java
index 9a9ad0a..5c208de 100644
--- a/src/org/broad/igv/util/ParsingUtils.java
+++ b/src/org/broad/igv/util/ParsingUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -29,17 +29,14 @@ import org.broad.igv.renderer.LineplotRenderer;
 import org.broad.igv.renderer.ScatterplotRenderer;
 import org.broad.igv.track.TrackProperties;
 import org.broad.igv.track.WindowFunction;
-import org.broad.igv.ui.IGVModel;
 import org.broad.igv.ui.util.MessageUtils;
 
 import java.awt.*;
 import java.io.*;
+import java.net.MalformedURLException;
 import java.net.URL;
-import java.net.URLConnection;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.zip.GZIPInputStream;
 
 /**
@@ -53,10 +50,10 @@ public class ParsingUtils {
             throws FileNotFoundException, IOException {
         BufferedReader reader = null;
 
-        if (pathOrUrl.startsWith("http:") || pathOrUrl.startsWith("file:")) {
+        if (pathOrUrl.startsWith("http:") || pathOrUrl.startsWith("https:") ||
+                pathOrUrl.startsWith("file:")) {
             URL url = new URL(pathOrUrl);
-            URLConnection connection = url.openConnection();
-            reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+            reader = new BufferedReader(new InputStreamReader(IGVHttpUtils.openConnectionStream(url)));
         } else {
             File file = new File(pathOrUrl);
 
@@ -78,9 +75,10 @@ public class ParsingUtils {
         AsciiLineReader reader = null;
         try {
             long fileLength = 0;
-            if (filename.startsWith("http")) {
+            //TODO -- ftp
+            if (filename.startsWith("http:") || filename.startsWith("https:")) {
                 URL url = new URL(filename);
-                fileLength = Long.parseLong(HttpUtils.getHeaderField(url, "Content-length"));
+                fileLength = Long.parseLong(IGVHttpUtils.getHeaderField(url, "Content-length"));
             } else {
                 fileLength = (new File(filename)).length();
             }
@@ -89,10 +87,21 @@ public class ParsingUtils {
             reader = openAsciiReader(new ResourceLocator(filename));
             String nextLine;
             int lines = 0;
+            // Skip the first 10 lines (headers, etc)
+            int nSkip = 10;
+            while (nSkip-- > 0 && reader.readLine() != null) {
+            }
+            long startPos = reader.getPosition();
+
             while ((nextLine = reader.readLine()) != null & lines < 100) {
                 lines++;
             }
-            double bytesPerLine = ((double) reader.getPosition()) / lines;
+
+            if (lines == 0) {
+                return 1000;
+            }
+
+            double bytesPerLine = (double) ((reader.getPosition() - startPos) / lines);
             int nLines = (int) (fileLength / bytesPerLine);
             return nLines;
 
@@ -114,7 +123,7 @@ public class ParsingUtils {
      * @throws IOException
      */
     public static AsciiLineReader openAsciiReader(ResourceLocator locator)
-            throws FileNotFoundException, IOException {
+            throws IOException {
         InputStream stream = openInputStream(locator);
         return new AsciiLineReader(stream);
 
@@ -129,20 +138,19 @@ public class ParsingUtils {
      * @throws IOException
      */
     public static InputStream openInputStream(ResourceLocator locator)
-            throws FileNotFoundException, IOException {
+            throws IOException {
 
         if (locator.getServerURL() != null) {
-            URL url = new URL(
-                    locator.getServerURL() + "?method=getContents&file=" + locator.getPath());
-            URLConnection connection = url.openConnection();
+            URL url = new URL(locator.getServerURL() + "?method=getContents&file=" + locator.getPath());
+            InputStream is = IGVHttpUtils.openConnectionStream(url);
 
             // Note -- assumption that url stream is compressed!
             try {
-                return new GZIPInputStream(connection.getInputStream());
+                return new GZIPInputStream(is);
             } catch (Exception ex) {
                 log.error("Error with gzip stream", ex);
                 throw new RuntimeException(
-                        "There was a server error loading file: " + locator.getDisplayName() +
+                        "There was a server error loading file: " + locator.getTrackName() +
                                 ". Please report to igv-help at broadinstitute.org");
 
             }
@@ -150,10 +158,10 @@ public class ParsingUtils {
         } else {
 
             InputStream inputStream = null;
-            if (locator.getPath().startsWith("http:") || locator.getPath().startsWith("file:")) {
+            if (locator.getPath().startsWith("http:") || locator.getPath().startsWith("https:") ||
+                    locator.getPath().startsWith("ftp:") || locator.getPath().startsWith("file:")) {
                 URL url = new URL(locator.getPath());
-                URLConnection connection = url.openConnection();
-                inputStream = connection.getInputStream();
+                inputStream = IGVHttpUtils.openConnectionStream(url);
             } else {
                 File file = new File(locator.getPath());
                 inputStream = new FileInputStream(file);
@@ -195,18 +203,16 @@ public class ParsingUtils {
 
         }
 
-        // Add the trailing string,  if there is room and if it is not empty.
+        // Add the trailing string 
         if (nTokens < maxTokens) {
             String trailingString = aString.substring(start);
-            if (trailingString.length() > 0) {
-                tokens[nTokens++] = trailingString;
-            }
+            tokens[nTokens++] = trailingString;
         }
         return nTokens;
     }
 
     /**
-     * Split the string into tokesn separated by tab or space.  This method
+     * Split the string into tokesn separated by tab or space(s).  This method
      * was added so support wig and bed files, which apparently accept
      * either.
      *
@@ -219,36 +225,29 @@ public class ParsingUtils {
         int maxTokens = tokens.length;
         int nTokens = 0;
         int start = 0;
-        int end = aString.indexOf('\t');
-        if (end < 0) {
-            end = aString.indexOf(' ');
-        }
-        if (end < 0) {
-            tokens[nTokens++] = aString;
-            return nTokens;
-        }
-        while ((end > 0) && (nTokens < maxTokens)) {
+        int tabEnd = aString.indexOf('\t');
+        int spaceEnd = aString.indexOf(' ');
+        int end = tabEnd < 0 ? spaceEnd : spaceEnd < 0 ? tabEnd : Math.min(spaceEnd, tabEnd);
+        while  ((end > 0) && (nTokens < maxTokens)) {
             //tokens[nTokens++] = new String(aString.toCharArray(), start, end-start); //  aString.substring(start, end);
-            tokens[nTokens++] = aString.substring(start, end).trim();
-            // Eat up whitespace
+            tokens[nTokens++] = aString.substring(start, end);
+
             start = end + 1;
-            while(start < aString.length() &&
-                    (aString.charAt(start) == ' ' || aString.charAt(start) == '\t')) {
+            // Gobble up any whitespace before next token -- don't gobble tabs, consecutive tabs => empty cell
+            while (start < aString.length() && aString.charAt(start) == ' ') {
                 start++;
             }
-            end = aString.indexOf('\t', start);
-            if (end < 0) {
-                end = aString.indexOf(' ', start);
-            }
+
+            tabEnd = aString.indexOf('\t', start);
+            spaceEnd = aString.indexOf(' ', start);
+            end = tabEnd < 0 ? spaceEnd : spaceEnd < 0 ? tabEnd : Math.min(spaceEnd, tabEnd);
 
         }
 
-        // Add the trailing string,  if there is room and if it is not empty.
+        // Add the trailing string
         if (nTokens < maxTokens) {
-            String trailingString = aString.substring(start);
-            if (trailingString.length() > 0) {
-                tokens[nTokens++] = trailingString;
-            }
+            String trailingString = aString.substring(start).trim();
+            tokens[nTokens++] = trailingString;
         }
         return nTokens;
     }
@@ -369,7 +368,6 @@ public class ParsingUtils {
     }
 
 
-
     /**
      * graphType         bar|points           # default is bar
      * yLineMark         real-value           # default is 0.0
@@ -389,7 +387,7 @@ public class ParsingUtils {
             // track type=wiggle_0 name="CSF +" description="CSF +" visibility=full autoScale=off viewLimits=-50:50
             List<String> tokens = StringUtils.breakQuotedString(nextLine, ' ');
             for (String pair : tokens) {
-                List<String>  kv = StringUtils.breakQuotedString(pair, '=');
+                List<String> kv = StringUtils.breakQuotedString(pair, '=');
                 if (kv.size() == 2) {
                     foundProperties = true;
                     String key = kv.get(0).toLowerCase().trim();
@@ -407,6 +405,10 @@ public class ParsingUtils {
                         trackProperties.setName(value);
                     } else if (key.equals("description")) {
                         trackProperties.setDescription(value);
+                    } else if (key.equals("itemrgb")) {
+                        trackProperties.setItemRGB(value.toLowerCase().equals("on"));
+                    } else if (key.equals("usescore")) {
+                        trackProperties.setUseScore(value.equals("1"));
                     } else if (key.equals("color")) {
                         Color color = ColorUtilities.convertRGBStringToColor(value);
                         trackProperties.setColor(color);
@@ -423,10 +425,13 @@ public class ParsingUtils {
 
                         // Ignore the min and max
                         String[] maxDefMin = value.split(":");
-                        trackProperties.setHeight(Integer.parseInt(maxDefMin[1].trim()));
-                        trackProperties.setMinHeight(Integer.parseInt(maxDefMin[2].trim()));
+                        trackProperties.setHeight(Integer.parseInt(maxDefMin[0].trim()));
+                        trackProperties.setMinHeight(Integer.parseInt(maxDefMin[1].trim()));
 
+                    } else if (key.equals("url")) {
+                        trackProperties.setUrl(value);
                     } else if (key.equals("graphtype")) {
+
                         if (value.equals("bar")) {
                             trackProperties.setRendererClass(BarChartRenderer.class);
                         } else if (value.equals("points")) {
@@ -439,18 +444,26 @@ public class ParsingUtils {
                     } else if (key.toLowerCase().equals("viewlimits")) {
                         String[] limits = value.split(":");
                         if (limits.length == 2) {
-                            float min = Float.parseFloat(limits[0].trim());
-                            float max = Float.parseFloat(limits[1].trim());
-                            trackProperties.setMinValue(min);
-                            trackProperties.setMaxValue(max);
+                            try {
+                                float min = Float.parseFloat(limits[0].trim());
+                                float max = Float.parseFloat(limits[1].trim());
+                                trackProperties.setMinValue(min);
+                                trackProperties.setMaxValue(max);
+                            } catch (NumberFormatException e) {
+                                log.error("viewLimits values must be numeric: " + value);
+                            }
                         }
                     } else if (key.equals("midrange")) {
                         String[] limits = value.split(":");
                         if (limits.length == 2) {
-                            float from = Float.parseFloat(limits[0].trim());
-                            float to = Float.parseFloat(limits[1].trim());
-                            trackProperties.setNeutralFromValue(from);
-                            trackProperties.setNeutralToValue(to);
+                            try {
+                                float from = Float.parseFloat(limits[0].trim());
+                                float to = Float.parseFloat(limits[1].trim());
+                                trackProperties.setNeutralFromValue(from);
+                                trackProperties.setNeutralToValue(to);
+                            } catch (NumberFormatException e) {
+                                log.error("midrange values must be numeric: " + value);
+                            }
                         }
                     } else if (key.equals("ylinemark")) {
                         try {
@@ -480,15 +493,55 @@ public class ParsingUtils {
                             trackProperties.setWindowingFunction(WindowFunction.percentile90);
 
                         }
+                    } else if(key.equals("maxfeaturewindow") || key.equals("featurevisibilitywindow")) {
+                        // These options are deprecated.  Use visibilityWindow
+                        try {
+                            int windowSize = Integer.parseInt(value);
+                            trackProperties.setFeatureVisibilityWindow(windowSize);
+                        } catch (NumberFormatException e) {
+                            log.error("featureVisibilityWindow must be numeric: " + nextLine);
+                        }
+
+                    } else if(key.equals("visibilitywindow")) {
+                        try {
+                            int windowSize = Integer.parseInt(value) * 1000;
+                            trackProperties.setFeatureVisibilityWindow(windowSize);
+                        } catch (NumberFormatException e) {
+                            log.error("featureVisibilityWindow must be an integer: " + nextLine);
+                        }
+
+                    }
+                    else if(key.equals("scaletype")) {
+                        if(value.equals("log")) {
+                            trackProperties.setLogScale(true);
+                        }
                     }
 
                 }
             }
 
-        } catch (Exception exception) {
+        }
+
+        catch (
+                Exception exception
+                )
+
+        {
             MessageUtils.showMessage("Error parsing track line: " + nextLine + " (" + exception.getMessage() + ")");
         }
+
         return foundProperties;
 
     }
+
+    public static  boolean pathExists(String covPath)  {
+        try {
+            return (new File(covPath)).exists() ||
+                    ((covPath.startsWith("http:") || covPath.startsWith("https:")) &&
+                            IGVHttpUtils.resourceAvailable(new URL(covPath)));
+        } catch (MalformedURLException e) {
+            // todo -- log
+            return false;
+        }
+    }
 }
diff --git a/src/org/broad/igv/util/ResourceLocator.java b/src/org/broad/igv/util/ResourceLocator.java
index 326ad47..38cea68 100644
--- a/src/org/broad/igv/util/ResourceLocator.java
+++ b/src/org/broad/igv/util/ResourceLocator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -17,6 +17,7 @@
  */
 package org.broad.igv.util;
 
+import java.awt.*;
 import java.io.File;
 
 /**
@@ -26,36 +27,17 @@ import java.io.File;
  */
 public class ResourceLocator {
 
-    /**
-     * Name (label) of this locator.
-     */
-    String name;
-    /**
-     * URL for the remote data server.  Null for local files
-     */
-    String serverURL;
-    /**
-     * The path for the file or resource.  For remote resource this value
-     * will be interpreted by the server and need not be the actualy, physical
-     * path.
-     */
-    String path;
-    /**
-     * A hyperlink to information.
-     */
-    String hyperLink;
-    /**
-     * A hyperlink to information.
-     */
-    String description;
-
-    public String getType() {
-        return type;
-    }
-
+    String serverURL; //URL for the remote data server.  Null for local files
+    String path; //The path for the file or resource.
+    String name;  // Display name.  Also used as a key for the sample info file
+    String infolink; // A hyperlink to general information about the track.
+    String url; //A URL pattern (UCSC convention) to a specific URL applicable to each feature
+    String description; //Descriptive text
     String type;
-
     String coverage;
+    String trackLine;  // A UCSC style track line.  Overrides value in file, if any.
+    Color color;
+    private String sampleId;
 
     /**
      * Constructor for local files
@@ -63,8 +45,7 @@ public class ResourceLocator {
      * @param path
      */
     public ResourceLocator(String path) {
-        this.serverURL = null;
-        this.path = path;
+        this(null, path);
     }
 
     /**
@@ -80,12 +61,11 @@ public class ResourceLocator {
         if (serverURL != null) {
             this.serverURL = serverURL.replace("broad.mit.edu", "broadinstitute.org");
         }
-        this.path = path;
-    }
-
-
-    public void setType(String type) {
-        this.type = type;
+        if (path != null && path.startsWith("file://")) {
+            this.path = path.substring(7);
+        } else {
+            this.path = path;
+        }
     }
 
     /**
@@ -128,31 +108,41 @@ public class ResourceLocator {
         return hash;
     }
 
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getType() {
+        return type;
+    }
 
     public String toString() {
         return path + (serverURL == null ? "" : " " + serverURL);
     }
 
-
     public String getPath() {
         return path;
     }
 
+    public String getFileName() {
+        return (new File(path)).getName();
+    }
+
     public String getServerURL() {
         return serverURL;
     }
 
     public boolean isLocal() {
-        return serverURL == null && !(path.startsWith("http")) &&
-                (type == null || (!type.equals("DAS") && !type.equals("URL")));
+        return serverURL == null && !(path.toLowerCase().startsWith("http:") ||
+                path.toLowerCase().startsWith("https:") || path.toLowerCase().startsWith("ftp:"));
     }
 
-    public void setHyperLink(String hyperLink) {
-        this.hyperLink = hyperLink;
+    public void setInfolink(String infolink) {
+        this.infolink = infolink;
     }
 
-    public String getHyperLink() {
-        return hyperLink;
+    public String getInfolink() {
+        return infolink;
     }
 
     public String getDescription() {
@@ -163,21 +153,19 @@ public class ResourceLocator {
         this.description = description;
     }
 
-    public String getDisplayName() {
-        return name == null ? new File(getPath()).getName() : name;
+    public String getTrackName() {
+        return name != null ? name : new File(getPath()).getName();
     }
 
-    /**
-     * Name (label) of this locator.
-     */
-    public String getName() {
-        return name;
-    }
 
     public void setName(String name) {
         this.name = name;
     }
 
+    public String getName() {
+        return name;
+    }
+
     public String getCoverage() {
         return coverage;
     }
@@ -186,18 +174,54 @@ public class ResourceLocator {
         this.coverage = coverage;
     }
 
+    public Color getColor() {
+        return color;
+    }
+
+    public void setColor(Color color) {
+        this.color = color;
+    }
+
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getTrackLine() {
+        return trackLine;
+    }
+
+    public void setTrackLine(String trackLine) {
+        this.trackLine = trackLine;
+    }
+
+    public String getSampleId() {
+        return sampleId;
+    }
+
+    public void setSampleId(String sampleId) {
+        this.sampleId = sampleId;
+    }
+
 
     /**
-     * ResourceLocator Attribute types
+     * FOR LOAD FROM SERVER (LEGACY)
      */
     public static enum AttributeType {
 
+        SERVER_URL("serverURL"),
+        PATH("path"),
         DESCRIPTION("description"),
         HYPERLINK("hyperlink"),
+        SAMPLE_ID("sampleID"),
         NAME("name"),
-        SERVER_URL("serverURL"),
-        PATH("path"),
+        URL("url"),
         RESOURCE_TYPE("resourceType"),
+        TRACK_LINE("trackLine"),
         COVERAGE("coverage");
 
         private String name;
@@ -215,24 +239,5 @@ public class ResourceLocator {
             return getText();
         }
 
-        static public AttributeType findEnum(String value) {
-
-            if (value == null) {
-                return null;
-            }
-
-            if (value.equals(SERVER_URL.getText())) {
-                return SERVER_URL;
-            } else if (value.equals(PATH.getText())) {
-                return PATH;
-            } else if (value.equals(DESCRIPTION.getText())) {
-                return DESCRIPTION;
-            } else if (value.equals(HYPERLINK.getText())) {
-                return HYPERLINK;
-            } else if (value.equals(NAME.getText())) {
-                return NAME;
-            }
-            return null;
-        }
     }
 }
diff --git a/src/org/broad/igv/util/RuntimeUtils.java b/src/org/broad/igv/util/RuntimeUtils.java
index a55b443..26d3deb 100644
--- a/src/org/broad/igv/util/RuntimeUtils.java
+++ b/src/org/broad/igv/util/RuntimeUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/SOLIDUtils.java b/src/org/broad/igv/util/SOLIDUtils.java
new file mode 100644
index 0000000..72b9dc2
--- /dev/null
+++ b/src/org/broad/igv/util/SOLIDUtils.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.util;
+
+/**
+ * User: jrobinso
+ * Date: Feb 21, 2010
+ */
+public class SOLIDUtils {
+    /**
+     * Return the sequence in Color Space (SOLID alignment encoding)
+     */
+    public static byte[] convertToColorSpace(byte[] baseSequence) {
+        // We need to know the base just to the left of the start
+
+        if (baseSequence == null || baseSequence.length == 0) {
+            return baseSequence;
+        }
+
+        byte[] csSequence = new byte[baseSequence.length];
+        // Can't know the first base
+        csSequence[0] = baseSequence[0]; //
+        int c1 = baseToCS(baseSequence[0]);
+        for (int i = 1; i < baseSequence.length; i++) {
+            int c2 = baseToCS(baseSequence[i]);
+            if (c2 == 4 && c1 != 4) {
+              csSequence[i] = 4;
+            } else if (c1 == 4 && c2 != 4) {
+              csSequence[i] = 5;
+            } else if (c1 == 4 && c2 == 4) {
+             csSequence[i] = 6;
+            } else {
+                csSequence[i] = (byte) (c1 ^ c2);
+            }
+            c1 = c2;
+        }
+        return csSequence;
+
+    }
+
+    public static int baseToCS(byte base) {
+        switch (base) {
+            case 'A':
+            case 'a':
+                return 0;
+            case 'C':
+            case 'c':
+                return 1;
+            case 'G':
+            case 'g':
+                return 2;
+            case 'T':
+            case 't':
+                return 3;
+            case 'N':
+            case 'n':
+                return 4;
+        }
+        return -1;
+    }
+}
diff --git a/src/org/broad/igv/util/SeekableFTPStream.java b/src/org/broad/igv/util/SeekableFTPStream.java
new file mode 100644
index 0000000..ebc4a54
--- /dev/null
+++ b/src/org/broad/igv/util/SeekableFTPStream.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.util;
+
+import org.apache.commons.net.ftp.FTP;
+import org.apache.commons.net.ftp.FTPClient;
+import org.apache.commons.net.ftp.FTPReply;
+import org.apache.log4j.Logger;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * User: jrobinso
+ * Date: Apr 13, 2010
+ */
+public class SeekableFTPStream extends SeekableStream {
+
+    static Logger log = Logger.getLogger(SeekableHTTPStream.class);
+
+    private long position = 0;
+    private String host;
+    private String path;
+    FTPClient ftp = null;
+
+     SeekableFTPStream(URL url) throws IOException {
+        this.host = url.getHost();
+        this.path = url.getPath();
+        ftp = openClient(host);
+        ftp.setFileType(FTP.BINARY_FILE_TYPE);
+
+    }
+
+
+    public void seek(long position) {
+        this.position = position;
+    }
+
+    public long position() {
+        return position;
+    }
+
+
+    @Override
+    public long skip(long n) throws IOException {
+        long bytesToSkip = n;
+        position += bytesToSkip;
+        return bytesToSkip;
+    }
+
+
+    @Override
+    public int read(byte[] buffer, int offset, int len) throws IOException {
+
+        if (offset < 0 || len < 0 || (offset + len) > buffer.length) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        if (len == 0) {
+            return 0;
+        }
+
+
+        int n = 0;
+        InputStream is = null;
+
+        try {
+
+            ftp.setRestartOffset(position);
+
+            is = ftp.retrieveFileStream(path);
+            System.out.println(ftp.getReplyString());
+
+            while (n < len) {
+                int count = is.read(buffer, offset + n, len - n);
+                if (count < 0) {
+                    if (n == 0) {
+                        return -1;
+                    } else {
+                        break;
+                    }
+                }
+                n += count;
+            }
+
+            position += n;
+
+            return n;
+
+        }
+
+        catch (EOFException e) {
+            if (n < 0) {
+                return -1;
+            } else {
+                position += n;
+                return n;
+            }
+
+        }
+
+        finally {
+            if (is != null) {
+                is.close();
+            }
+            ftp.completePendingCommand();
+        }
+    }
+
+
+    public void close() throws IOException {
+        // Nothing to do
+    }
+
+    public int read() throws IOException {
+        throw new UnsupportedOperationException("read() is not supported on SeekableHTTPStream.  Must read in blocks.");
+    }
+
+
+    private FTPClient openClient(String host) throws IOException {
+
+        FTPClient ftp = new FTPClient();
+        ftp.connect(host);
+        System.out.println(ftp.getReplyString());
+
+        // After connection attempt, you should check the reply code to verify
+        // success.
+        int reply = ftp.getReplyCode();
+
+        if (!FTPReply.isPositiveCompletion(reply)) {
+            ftp.disconnect();
+            System.err.println("FTP server refused connection.");
+            throw new RuntimeException("FTP server refused connection.");
+        }
+
+        boolean success = ftp.login("anonymous", "igv-team at broadinstitute.org");
+        if (!success) {
+            System.err.println("FTP login failed " + ftp.getReplyString());
+            throw new RuntimeException("FTP login failed " + ftp.getReplyString());
+        }
+
+        // Use passive mode as default because most of us are
+        // behind firewalls these days.
+        ftp.enterLocalPassiveMode();
+
+
+        return ftp;
+    }
+
+}
diff --git a/src/org/broad/igv/util/SeekableFileStream.java b/src/org/broad/igv/util/SeekableFileStream.java
index 9bb79fa..c6a9540 100644
--- a/src/org/broad/igv/util/SeekableFileStream.java
+++ b/src/org/broad/igv/util/SeekableFileStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -35,70 +35,86 @@ public class SeekableFileStream extends SeekableStream {
     File file;
     FileInputStream fis;
 
-    public SeekableFileStream(File file) throws FileNotFoundException {
+    SeekableFileStream(File file) throws FileNotFoundException {
         this.file = file;
         fis = new FileInputStream(file);
     }
 
-    public long length() {
-        return file.length();
-    }
-
-    public boolean eof() throws IOException {
-        return file.length() == fis.getChannel().position();
-    }
-
     public void seek(long position) throws IOException {
+        if (fis == null) {
+            fis = new FileInputStream(file);
+        }
         fis.getChannel().position(position);
     }
 
-    public long position() throws IOException  {
+    public long position() throws IOException {
+        if (fis == null) {
+            fis = new FileInputStream(file);
+        }
         return fis.getChannel().position();
     }
 
     @Override
     public long skip(long n) throws IOException {
-        fis.getChannel().position(fis.getChannel().position() + n);
-        return n;
+        if (fis == null) {
+            fis = new FileInputStream(file);
+        }
+        return fis.skip(n);
     }
 
+    @Override
     public int read(byte[] buffer, int offset, int length) throws IOException {
-
-        if (length < 0 || offset < 0 || offset + length > buffer.length) {
-            throw new IndexOutOfBoundsException();
+        if (fis == null) {
+            fis = new FileInputStream(file);
         }
+        return fis.read(buffer, offset, length);
+    }
 
-        if(length == 0) {
-            return 0;
+    @Override
+    public int read() throws IOException {
+        if (fis == null) {
+            fis = new FileInputStream(file);
         }
+        return fis.read();
+    }
 
-        int n = 0;
-        while (n < length) {
-            int count = fis.read(buffer, offset + n, length - n);
-            if (count < 0) {
-                return (n == 0 ? -1 : n);
-            }
-            n += count;
+    @Override
+    public int read(byte[] b) throws IOException {
+        if (fis == null) {
+            fis = new FileInputStream(file);
         }
-        return n;
-
+        return fis.read(b);
     }
 
+    @Override
+    public int available() throws IOException {
+        if (fis == null) {
+            fis = new FileInputStream(file);
+        }
+        return fis.available();
+    }
 
-    public void close() throws IOException {
-        fis.close();
-
+    @Override
+    public void mark(int readlimit) {
+        fis.mark(readlimit);
     }
 
-    public byte[] readBytes(long position, int nBytes) throws IOException {
-        seek(position);
+    @Override
+    public boolean markSupported() {
+        return fis.markSupported();
+    }
 
-        byte[] buffer = new byte[nBytes];
-        read(buffer, 0, nBytes);
-        return buffer;
+    @Override
+    public void reset() throws IOException {
+        fis.reset();
     }
 
-    public int read() throws IOException {
-        return fis.read();
+    @Override
+    public void close() throws IOException {
+        if (fis != null) {
+            fis.close();
+            fis = null;
+        }
     }
+
 }
diff --git a/src/org/broad/igv/util/SeekableFileStreamTest.java b/src/org/broad/igv/util/SeekableFileStreamTest.java
deleted file mode 100644
index 6b5a43d..0000000
--- a/src/org/broad/igv/util/SeekableFileStreamTest.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.broad.igv.util;
-
-import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-
-import java.io.File;
-
-/**
- * Created by IntelliJ IDEA.
- * User: jrobinso
- * Date: Dec 6, 2009
- * Time: 1:07:42 AM
- * To change this template use File | Settings | File Templates.
- */
-public class SeekableFileStreamTest {
-
-
-    @Test
-    public void testSeek() throws Exception {
-        String expectedLine = "chr22\t14000000\t15000000\trecombRate\t0.182272\t0.20444\t0.160104\t0\t0\t0\t0\t0\t0";
-        File testFile = new File("test/data/recombRate.igv.txt");
-        SeekableFileStream is = new SeekableFileStream(testFile);
-        is.seek(149247);
-        AsciiLineReader reader = new AsciiLineReader(is);
-        String nextLine = reader.readLine();
-        assertEquals(expectedLine, nextLine);
-    }
-}
diff --git a/src/org/broad/igv/util/SeekableHTTPStream.java b/src/org/broad/igv/util/SeekableHTTPStream.java
index ed323a3..f3d0055 100644
--- a/src/org/broad/igv/util/SeekableHTTPStream.java
+++ b/src/org/broad/igv/util/SeekableHTTPStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -23,14 +23,16 @@
 package org.broad.igv.util;
 
 import org.apache.log4j.Logger;
-import org.broad.igv.PreferenceManager;
 
+import java.io.BufferedInputStream;
+import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.util.List;
 import java.util.Map;
+import java.util.HashMap;
 
 /**
  * @author jrobinso
@@ -38,24 +40,21 @@ import java.util.Map;
 public class SeekableHTTPStream extends SeekableStream {
 
     static Logger log = Logger.getLogger(SeekableHTTPStream.class);
-
-    private long position = 0;
-    private long contentLength = -1;
-    private URL url;
     private static final String WEBSERVICE_URL = "http://www.broadinstitute.org/webservices/igv";
     private static final String IGV_DATA_HOST = "www.broadinstitute.org";
     private static final String DATA_PATH = "/xchip/igv/data/public";
     private static final String DATA_HTTP_PATH = "/igvdata";
 
-    public SeekableHTTPStream(URL url) {
-        this.url = url;
 
-        if (log.isDebugEnabled()) {
-            log.debug("Creating SeekableHTTPStream for: " + url);
-        }
+    private long position = 0;
+    private long contentLength = -1;
+    private URL url;
+
+     SeekableHTTPStream(URL url) {
+        this.url = url;
 
         // Try to get the file length
-        String contentLengthString = HttpUtils.getHeaderField(url, "Content-Length");
+        String contentLengthString = IGVHttpUtils.getHeaderField(url, "Content-Length");
         if (contentLengthString != null) {
             try {
                 contentLength = Long.parseLong(contentLengthString);
@@ -66,14 +65,6 @@ public class SeekableHTTPStream extends SeekableStream {
         }
     }
 
-    public long length() {
-        return contentLength;
-    }
-
-    public boolean eof() throws IOException {
-        return position >= contentLength;
-    }
-
     public void seek(long position) {
         this.position = position;
     }
@@ -82,17 +73,26 @@ public class SeekableHTTPStream extends SeekableStream {
         return position;
     }
 
+
     @Override
     public long skip(long n) throws IOException {
-        position += n;
-        return n;
+        long bytesToSkip = n;
+        if (contentLength > 0) {
+            bytesToSkip = Math.min(n, contentLength - position);
+        }
+        position += bytesToSkip;
+        return bytesToSkip;
     }
 
+
+    @Override
     public int read(byte[] buffer, int offset, int len) throws IOException {
+        boolean useByteRange = IGVHttpUtils.useByteRange();
+        return read(buffer, offset, len, useByteRange);
+    }
+    
+    public int read(byte[] buffer, int offset, int len, boolean useByteRange) throws IOException {
 
-        if (log.isDebugEnabled()) {
-            log.debug("read: " + offset + " " + len);
-        }
         if (offset < 0 || len < 0 || (offset + len) > buffer.length) {
             throw new IndexOutOfBoundsException();
         }
@@ -101,7 +101,6 @@ public class SeekableHTTPStream extends SeekableStream {
             return 0;
         }
 
-        boolean useByteRange = PreferenceManager.getInstance().isUseByteRange();
 
         // Try webservice first,  if so indicated.
         if (!useByteRange) {
@@ -111,39 +110,30 @@ public class SeekableHTTPStream extends SeekableStream {
             }
         }
 
-        HttpURLConnection connection = null;
+        //HttpURLConnection connection = null;
         InputStream is = null;
         String byteRange = "";
         int n = 0;
         try {
-
-            if (log.isDebugEnabled()) {
-                log.debug("Opening connection to: " + url.toString());
+            Map<String, String> props = new HashMap();
+            long endRange = position + len - 1;
+            // IF we know the total content length, limit the end range to that.
+            if (contentLength > 0) {
+                endRange = Math.min(endRange, contentLength);
             }
+            byteRange = "bytes=" + position + "-" + endRange;
+            props.put("Range", byteRange);
 
-            connection = (HttpURLConnection) url.openConnection();
-
-            byteRange = "bytes=" + position + "-" + (position + len - 1);
-            if (log.isDebugEnabled()) {
-                log.debug("Setting byte range to: " + byteRange);
-            }
-            connection.setRequestProperty("Range", byteRange);
-
-            if (log.isDebugEnabled()) {
-                logHeaderFields(connection);
-                log.debug("Opening input stream: " + url.toString());
-            }
-            is = connection.getInputStream();
+            is = IGVHttpUtils.openHttpStream(url, props);
 
             while (n < len) {
                 int count = is.read(buffer, offset + n, len - n);
-
-                if (log.isDebugEnabled()) {
-                    log.debug("Count = " + count + " n = " + n + " length = " + len);
-                }
-
                 if (count < 0) {
-                    return (n == 0 ? -1 : n);
+                    if (n == 0) {
+                        return -1;
+                    } else {
+                        break;
+                    }
                 }
                 n += count;
             }
@@ -154,33 +144,58 @@ public class SeekableHTTPStream extends SeekableStream {
 
         }
 
+        catch (IOException e) {
+            // THis is a bit of a hack, but its not clear how else to handle this.  If a byte range is specified
+            // that goes past the end of the file the response code will be 416.  The MAC os translates this to
+            // an IOException with the 416 code in the message.  Windows translates the error to an EOFException.
+            //
+            //  The BAM file iterator  uses the return value to detect end of file (specifically looks for n == 0).
+            if (e.getMessage().contains("416") || (e instanceof EOFException)) {
+                if (n < 0) {
+                    return -1;
+                } else {
+                    position += n;
+                    // As we are at EOF, the contentLength and position are by definition =
+                    contentLength = position;
+                    return n;
+                }
+            } else {
+                throw e;
+            }
+        }
+
         finally {
             if (is != null) {
                 is.close();
             }
-            if (connection != null) {
-                connection.disconnect();
-            }
+            //if (connection != null) {
+            //    connection.disconnect();
+            //}
         }
     }
 
 
     public void close() throws IOException {
+        BufferedInputStream bis;
         // Nothing to do
     }
 
-
-    public byte[] readBytes(long position, int nBytes) throws IOException {
-        this.position = position;
-        byte[] buffer = new byte[nBytes];
-        read(buffer, 0, nBytes);
-        return buffer;
-    }
-
     public int read() throws IOException {
         throw new UnsupportedOperationException("read() is not supported on SeekableHTTPStream.  Must read in blocks.");
     }
 
+    private void logHeaderFields(HttpURLConnection connection) {
+        Map<String, List<String>> map = connection.getHeaderFields();
+
+        for (Map.Entry<String, List<String>> entry : map.entrySet()) {
+            System.out.print(entry.getKey() + ":\t");
+            for (String v : entry.getValue()) {
+                System.out.print(v + " ");
+            }
+            System.out.println();
+        }
+    }
+
 
     /**
      * Alternative to range byte requests.  Some organizations,  specifically Partners,  mangle range byte headers
@@ -254,16 +269,4 @@ public class SeekableHTTPStream extends SeekableStream {
     }
 
 
-    private void logHeaderFields(HttpURLConnection connection) {
-        Map<String, List<String>> map = connection.getHeaderFields();
-
-        for (Map.Entry<String, List<String>> entry : map.entrySet()) {
-            System.out.print(entry.getKey() + ":\t");
-            for (String v : entry.getValue()) {
-                System.out.print(v + " ");
-            }
-            System.out.println();
-        }
-    }
-
 }
diff --git a/src/org/broad/igv/util/SeekableServiceStream.java b/src/org/broad/igv/util/SeekableServiceStream.java
index 53f8573..b4f16a2 100644
--- a/src/org/broad/igv/util/SeekableServiceStream.java
+++ b/src/org/broad/igv/util/SeekableServiceStream.java
@@ -1,6 +1,25 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
 package org.broad.igv.util;
 
 import org.apache.log4j.Logger;
+import org.broad.igv.util.SeekableStream;
 
 import java.io.EOFException;
 import java.io.IOException;
@@ -27,7 +46,7 @@ public class SeekableServiceStream extends SeekableStream {
         this.locator = locator;
 
         // Try to get the file length
-        /*String contentLengthString = HttpUtils.getHeaderField(url, "Content-Length");
+        /*String contentLengthString = IGVHttpUtils.getHeaderField(url, "Content-Length");
         if (contentLengthString != null) {
             try {
                 contentLength = Long.parseLong(contentLengthString);
@@ -77,7 +96,7 @@ public class SeekableServiceStream extends SeekableStream {
         int n = 0;
         try {
 
-            connection = (HttpURLConnection) url.openConnection();
+            connection = IGVHttpUtils.openConnection(url);
             is = connection.getInputStream();
 
             while (n < length) {
diff --git a/src/org/broad/igv/util/SeekableSplitStream.java b/src/org/broad/igv/util/SeekableSplitStream.java
new file mode 100644
index 0000000..2736b5e
--- /dev/null
+++ b/src/org/broad/igv/util/SeekableSplitStream.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.util;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class to wrap
+ */
+public class SeekableSplitStream extends SeekableStream {
+
+    long position = 0;
+    long length = 0;
+    List<PartDescriptor> descriptors;
+    int currentStreamIndex = 0;
+
+     SeekableSplitStream(String path) throws IOException {
+        parseDescriptors(path);
+    }
+
+    public void seek(long position) throws IOException {
+        this.position = position;
+        long end = 0;
+        long start = 0;
+        for (PartDescriptor desc : descriptors) {
+            end += desc.getContentLength();
+            if (position >= start && position < end) {
+                long delta = position - start;
+                desc.getStream().seek(delta);
+            } else {
+                desc.getStream().seek(0);
+            }
+            start = end;
+        }
+    }
+
+    public long position() throws IOException {
+        return position;
+    }
+
+    @Override
+    public int read(byte[] buffer, int off, int len) throws IOException {
+
+        int bytesRead = 0;
+        long end = 0;
+        long start = 0;
+        for (PartDescriptor desc : descriptors) {
+            end += desc.getContentLength();
+            if (position >= start && position < end) {
+                int delta = (int) (desc.contentLength - desc.stream.position());
+                int l = Math.min(len - bytesRead, delta);
+                int nBytes = desc.stream.read(buffer, off + bytesRead, l);
+                if (nBytes < 0) {
+                    return nBytes;
+                }
+                bytesRead += nBytes;
+                position += nBytes;
+                if (bytesRead >= len) {
+                    break;
+                }
+            }
+            start = end;
+        }
+
+
+        // If we get this far, and haven't read any bytes, we're at EOF
+        return bytesRead > 0 ? bytesRead : -1;
+    }
+
+    @Override
+    public int read() throws IOException {
+        int b = descriptors.get(currentStreamIndex).getStream().read();
+        position++;
+        return b;
+    }
+
+    @Override
+    public void close() throws IOException {
+        for (PartDescriptor desc : descriptors) {
+            desc.getStream().close();
+        }
+    }
+
+    private void parseDescriptors(String path) throws IOException {
+
+        BufferedReader br = null;
+        descriptors = new ArrayList();
+        try {
+            br = org.broad.igv.util.ParsingUtils.openBufferedReader(path);
+            String nextLine;
+            while ((nextLine = br.readLine()) != null) {
+                String[] tokens = nextLine.split(" ");
+                if (tokens.length == 2) {
+                    String p = tokens[0];
+
+                    // Require the files are in the same directory as the list file
+                    String listFileName = null;
+                    if(path.startsWith("http:") || path.startsWith("https:")) {
+                       URL url = new URL(path);
+                       listFileName = (new File(url.getPath())).getName();
+                    }
+                    else {
+                        listFileName = (new File(path)).getName();
+                    }
+                    SeekableStream stream = SeekableStreamFactory.getStreamFor(path.replace(listFileName, p));
+                    
+                    long length = Long.parseLong(tokens[1]);
+                    descriptors.add(new SeekableSplitStream.PartDescriptor(length, stream));
+                } else {
+                    // TODO -- throw exception, or warning?
+                }
+            }
+        }
+        finally {
+            if (br != null) {
+                br.close();
+            }
+        }
+    }
+
+
+
+    public static class PartDescriptor {
+        private long contentLength;
+        private SeekableStream stream;
+
+        public PartDescriptor(long contentLength, SeekableStream stream) {
+            this.contentLength = contentLength;
+            this.stream = stream;
+        }
+
+        public long getContentLength() {
+            return contentLength;
+        }
+
+        public SeekableStream getStream() {
+            return stream;
+        }
+    }
+}
diff --git a/src/org/broad/igv/util/SeekableStream.java b/src/org/broad/igv/util/SeekableStream.java
index 79c0498..3194d39 100644
--- a/src/org/broad/igv/util/SeekableStream.java
+++ b/src/org/broad/igv/util/SeekableStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -15,20 +15,37 @@
  * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
  * FOREGOING.
  */
+
 package org.broad.igv.util;
 
+import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
 
+/**
+ * User: jrobinso
+ * Date: Nov 29, 2009
+ */
 public abstract class SeekableStream extends InputStream {
 
-    public abstract long length();
-
     public abstract void seek(long position) throws IOException;
 
     public abstract long position() throws IOException;
 
-    public abstract byte[] readBytes(long position, int nBytes) throws IOException;
+    /**
+     * Read enough bytes to fill the input buffer
+     */
+    public void readFully(byte b[]) throws IOException {
+        int len = b.length;
+        if (len < 0)
+            throw new IndexOutOfBoundsException();
+        int n = 0;
+        while (n < len) {
+            int count = read(b, n, len - n);
+            if (count < 0)
+                throw new EOFException();
+            n += count;
+        }
+    }
 
-    public abstract boolean eof() throws IOException;
 }
diff --git a/src/org/broad/igv/util/SeekableStreamFactory.java b/src/org/broad/igv/util/SeekableStreamFactory.java
index 997cb40..35d9970 100644
--- a/src/org/broad/igv/util/SeekableStreamFactory.java
+++ b/src/org/broad/igv/util/SeekableStreamFactory.java
@@ -1,9 +1,26 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
 package org.broad.igv.util;
 
 import java.io.File;
-import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.net.URL;
-import java.net.MalformedURLException;
 
 /**
  * Created by IntelliJ IDEA.
@@ -14,16 +31,22 @@ import java.net.MalformedURLException;
  */
 public class SeekableStreamFactory {
 
-    public static SeekableStream getStreamFor(ResourceLocator locator) throws FileNotFoundException, MalformedURLException {
+    public static SeekableStream getStreamFor(String path) throws IOException {
 
-        if (locator.getServerURL() != null) {
-            return new SeekableServiceStream(locator);
+        if (path.endsWith(".list")) {
+            return new SeekableSplitStream(path);
 
         } else {
-            String path = locator.getPath();
-            return path.startsWith("http:") ?
-                    new SeekableHTTPStream(new URL(path)) :
-                    new SeekableFileStream(new File(path));
+            SeekableStream is = null;
+            if (path.toLowerCase().startsWith("http:") || path.toLowerCase().startsWith("https:")) {
+                is = new SeekableHTTPStream(new URL(path));
+            } else if (path.toLowerCase().startsWith("ftp:")) {
+                is = new SeekableFTPStream(new URL(path));
+            } else {
+                is = new SeekableFileStream(new File(path));
+            }
+            return is;
         }
     }
+
 }
diff --git a/src/org/broad/igv/util/SequenceGC.java b/src/org/broad/igv/util/SequenceGC.java
new file mode 100644
index 0000000..122317a
--- /dev/null
+++ b/src/org/broad/igv/util/SequenceGC.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.broad.igv.util;
+
+import jargs.gnu.CmdLineParser;
+import org.apache.log4j.Logger;
+import org.broad.igv.exceptions.DataLoadException;
+import static org.broad.igv.util.ParsingUtils.openBufferedReader;
+
+import java.io.*;
+import java.util.LinkedList;
+import java.util.Queue;
+
+public class SequenceGC {
+
+    private static int windowSize = 5;
+    private static int windowStep = 1;
+    private static String chromosome = "";
+    private static Logger log = Logger.getLogger(SequenceGC.class);
+
+    private static void printUsage() {
+        System.err.println(
+                "Usage: SequenceGC [{-f, --file} a_path] [{-o,--output} a_out_path]\n" +
+                        "                  [{-w,--window} a_integer] [{-s,--step} a_integer]");
+    }
+
+    public static void main(String args[]) {
+
+        CmdLineParser parser = new CmdLineParser();
+        CmdLineParser.Option inFile = parser.addStringOption('f', "file");
+        CmdLineParser.Option outFile = parser.addStringOption('o', "output");
+        CmdLineParser.Option window = parser.addIntegerOption('w', "window");
+        CmdLineParser.Option step = parser.addIntegerOption('s', "step");
+        try {
+            parser.parse(args);
+        }
+        catch (CmdLineParser.OptionException e) {
+            System.err.println(e.getMessage());
+            printUsage();
+            System.exit(2);
+        }
+
+        windowSize =
+                (Integer) parser.getOptionValue(window, 5);
+        windowStep =
+                (Integer) parser.getOptionValue(step, 1);
+
+        SequenceGC sequence = new SequenceGC();
+        String inPath = (String) parser.getOptionValue(inFile);
+        String outPath = (String) parser.getOptionValue(outFile);
+
+        if (!(inPath == null))
+            sequence.ProcessPath(inPath, outPath);
+        else
+            printUsage();
+    }
+
+    public SequenceGC(int windowSize, int step) {
+        this.windowSize = windowSize;
+        this.windowStep = step;
+    }
+
+    public SequenceGC(int windowSize) {
+        this.windowSize = windowSize;
+    }
+
+    private SequenceGC() {
+    }
+
+    public void ProcessPath(String inPath, String outPath) {
+
+        File iPath = new File(inPath);
+        File oPath = new File(outPath);
+        String oFile;
+        PrintWriter pw = null;
+
+        try {
+            oFile = oPath.getAbsolutePath();
+            if (!oFile.endsWith(".wig")) {
+                oFile = oFile + ".wig";
+            }
+            pw = new PrintWriter(new BufferedWriter(new FileWriter(oFile)));
+
+            //TODO Make a better output file naming convention.
+            if (iPath.isDirectory()) {
+                for (File file : new File(inPath).listFiles()) {
+                    if (file.isFile()) {
+                        if (file.getName().endsWith(".txt")) {
+                            chromosome = file.getName().replace(".txt", "");
+                            CalculatePercent(file.getAbsolutePath(), pw);
+                        } else {
+                            log.error("Could not load" + file.getAbsolutePath());
+                        }
+                    }
+                }
+            } else if (iPath.isFile()) {
+                if (iPath.getName().endsWith(".txt")) {
+                    chromosome = iPath.getName().replace(".txt", "");
+                    CalculatePercent(iPath.getAbsolutePath(), pw);
+                }
+            } else {
+                throw new DataLoadException("Unable to load files", inPath);
+            }
+        } catch (IOException e) {
+            log.error("Error during load", e);
+        } finally {
+            pw.close();
+        }
+    }
+
+    public void CalculatePercent(String inputFile, PrintWriter pw) {
+
+        Queue<Character> sequenceWindow = new LinkedList<Character>();
+        BufferedReader bfr = null;
+
+        int inRead;
+        int inWindow;
+        double result;
+        char fRead;
+        int startLocation = windowSize - (windowSize / 2);
+
+        try {
+            bfr = openBufferedReader(inputFile);
+            pw.println("fixedStep" + " chrom=" + chromosome + " start=" + startLocation + " step=" + windowStep);
+
+            while ((inRead = bfr.read()) != -1) {
+                fRead = (char) inRead;
+                if ((!(fRead == 'A' || fRead == 'T' || fRead == 'C' || fRead == 'G' || fRead == 'N'))
+                        && (!(fRead == 'a' || fRead == 't' || fRead == 'c' || fRead == 'g' || fRead == 'n'))) continue;
+
+                sequenceWindow.add(fRead);
+                if (sequenceWindow.size() == windowSize) {
+                    inWindow = 0;
+                    for (char aSequenceWindow : sequenceWindow) {
+                        if (aSequenceWindow == 'G' || aSequenceWindow == 'C') inWindow++;
+                    }
+                    if (inWindow == 0) {
+                        pw.println("0.0");
+                    } else {
+                        result = ((double) inWindow / (double) windowSize) * 100;
+                        pw.println(String.format("%.2f", result));
+                    }
+                    for (int i = 0; i < windowStep; i++)
+                        sequenceWindow.remove();
+                }
+            }
+        }
+        catch (IOException e) {
+            throw new DataLoadException(e.getMessage(), inputFile);
+        }
+        finally {
+            try {
+                bfr.close();
+            } catch (IOException e) {
+                log.error("Error closing files", e);
+            }
+        }
+    }
+}
diff --git a/src/org/broad/igv/util/StreamUtils.java b/src/org/broad/igv/util/StreamUtils.java
new file mode 100644
index 0000000..db7f06c
--- /dev/null
+++ b/src/org/broad/igv/util/StreamUtils.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.util;
+
+import org.apache.log4j.Logger;
+
+import java.io.*;
+
+/**
+ * User: jrobinso
+ * Date: Mar 21, 2010
+ */
+public class StreamUtils {
+    private static Logger log = Logger.getLogger(StreamUtils.class);
+
+    public static void closeQuitely(InputStream is) {
+        try {
+            is.close();
+        } catch (IOException e) {
+            log.error("Error closing stream", e);
+        }
+    }
+
+    public static void closeQuitely(OutputStream os) {
+        try {
+            os.close();
+        } catch (IOException e) {
+            log.error("Error closing stream", e);
+        }
+    }
+
+
+    public static void writeString(DataOutputStream os, String string) throws IOException {
+        os.write(string.getBytes());
+        os.write(0);
+    }
+
+    public static String readString(DataInputStream is) throws IOException {
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream(100);
+        byte b = -1;
+        while ((b = is.readByte()) != 0) {
+            bytes.write(b);
+        }
+        return new String(bytes.toByteArray());
+    }
+}
diff --git a/src/org/broad/igv/util/StringUtils.java b/src/org/broad/igv/util/StringUtils.java
index 9558b01..305663f 100644
--- a/src/org/broad/igv/util/StringUtils.java
+++ b/src/org/broad/igv/util/StringUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -18,6 +18,9 @@
 
 package org.broad.igv.util;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -68,4 +71,32 @@ public class StringUtils {
         return strings;
 
     }
+
+
+    public static short genoToShort(String genotype) {
+        byte[] bytes = genotype.getBytes();
+        return (short) ((bytes[0] & 0xff) << 8 | (bytes[1] & 0xff));
+    }
+
+    public static String readString(ByteBuffer byteBuffer) throws IOException {
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream(100);
+        byte b = -1;
+        while ((b = byteBuffer.get()) != 0) {
+            bytes.write(b);
+        }
+        return new String(bytes.toByteArray());
+    }
+
+    public static void main(String[] args) {
+
+        String genotype = "AC";
+        short genoShort = genoToShort(genotype);
+
+        char allel1 = (char) ((genoShort >>> 8) & 0xFF);
+        char allel2 = (char) ((genoShort >>> 0) & 0xFF);
+
+        System.out.println("Allele1: " + allel1);
+        System.out.println("Allele2: " + allel2);
+
+    }
 }
diff --git a/src/org/broad/igv/util/Temp.java b/src/org/broad/igv/util/Temp.java
new file mode 100644
index 0000000..bc4498d
--- /dev/null
+++ b/src/org/broad/igv/util/Temp.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.util;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: Jan 21, 2010
+ * Time: 10:43:09 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class Temp {
+
+
+    public static void main(String[] args) throws IOException, InterruptedException {
+        fixDicty();
+    }
+
+    public static void fixDicty() throws IOException, InterruptedException {
+
+        String dir = "/Users/jrobinso/IGV/dicty";
+        Map<String, String> chrMap = new HashMap();
+        chrMap.put("DDB0169550", "M");
+        chrMap.put("DDB0215018", "3F");
+        chrMap.put("DDB0215151", "2F");
+        chrMap.put("DDB0220052", "BF");
+        chrMap.put("DDB0232428", "1");
+        chrMap.put("DDB0232429", "2");
+        chrMap.put("DDB0232430", "3");
+        chrMap.put("DDB0232431", "4");
+        chrMap.put("DDB0232432", "5");
+        chrMap.put("DDB0232433", "6");
+        chrMap.put("DDB0237465", "R");
+
+
+        /*Iterator<Map.Entry<String, String>> iter = chrMap.entrySet().iterator();
+        Map.Entry<String, String> first = iter.next();
+        sedExpression.append("sed 's/" + first.getKey() + "/" + first.getValue() + "/g'  dicty.fa");
+        while(iter.hasNext()) {
+            first = iter.next();
+            sedExpression.append(" | ");
+            sedExpression.append("sed 's/" + first.getKey() + "/" + first.getValue() + "/g'");
+        }
+
+       sedExpression.append(" > dicty_chr.fa");
+       System.out.println(sedExpression.toString());
+        */
+
+        File gffDir = new File(dir, "gff3");
+
+        for (File f : gffDir.listFiles()) {
+            if (f.getName().endsWith("gff")) {
+                StringBuffer sedExpression = new StringBuffer();
+                Iterator<Map.Entry<String, String>> iter = chrMap.entrySet().iterator();
+                Map.Entry<String, String> first = iter.next();
+                sedExpression.append("sed 's/" + first.getKey() + "/" + first.getValue() + "/g' " + f.getAbsolutePath());
+                while (iter.hasNext()) {
+                    first = iter.next();
+                    sedExpression.append(" | ");
+                    sedExpression.append("sed 's/" + first.getKey() + "/" + first.getValue() + "/g'");
+                }
+                String outputFile = f.getAbsolutePath().replace("chromosome_", "chr");
+                String cmdLine = sedExpression.toString() + " > " + outputFile;
+                Process p = Runtime.getRuntime().exec(cmdLine);
+                p.waitFor();
+                System.out.println(cmdLine);
+
+                System.out.println(p.exitValue());
+            }
+        }
+
+        /*
+        Process p = Runtime.getRuntime().exec("cat");
+
+        OutputStream os = p.getOutputStream();
+
+        PrintWriter pw = new PrintWriter(new OutputStreamWriter(os));
+        pw.println("abcd") ;
+        pw.flush();
+        os.close();
+
+        InputStream is = p.getInputStream();
+        */
+
+    }
+
+
+    public static void makeSampleInfo() throws Exception {
+
+        Set<String> sec = new HashSet(Arrays.asList("TCGA-02-0010", "TCGA-02-0028", "TCGA-02-0102",
+                "TCGA-02-0114", "TCGA-08-0525"));
+
+        Set<String> treat = new HashSet(Arrays.asList("TCGA-02-0001", "TCGA-02-0007",
+                "TCGA-02-0010",
+                "TCGA-02-0014",
+                "TCGA-02-0021",
+                "TCGA-02-0024",
+                "TCGA-02-0028",
+                "TCGA-02-0043",
+                "TCGA-02-0057",
+                "TCGA-02-0058",
+                "TCGA-02-0080",
+                "TCGA-02-0083",
+                "TCGA-02-0089",
+                "TCGA-02-0099",
+                "TCGA-02-0102",
+                "TCGA-02-0107",
+                "TCGA-02-0113",
+                "TCGA-02-0114",
+                "TCGA-02-0116",
+                "TCGA-08-0517",
+                "TCGA-08-0525"));
+
+        Set<String> hyper = new HashSet(Arrays.asList("TCGA-02-0010",
+                "TCGA-02-0014",
+                "TCGA-02-0028",
+                "TCGA-02-0043",
+                "TCGA-02-0083",
+                "TCGA-02-0099",
+                "TCGA-02-0114",
+                "TCGA-02-0015",
+                "TCGA-02-0070"));
+
+
+        BufferedReader br = new BufferedReader(new FileReader("Public_IGV_TCGA_sample_info_20080827.txt"));
+        PrintWriter pw = new PrintWriter(new FileWriter("Sample_info.txt"));
+
+        String nextLine = br.readLine();
+        pw.println(nextLine + "\tTreated\tPrimary/Secondary\tHypermutated");
+
+        while ((nextLine = br.readLine()) != null) {
+            String[] tokens = nextLine.split("\t");
+            String sample = tokens[0].length() > 12 ? tokens[0].substring(0, 12) : tokens[0];
+            String t = treat.contains(sample) ? "Y" : "";
+            String s = sec.contains(sample) ? "Secondary" : "Primary";
+            String h = hyper.contains(sample) ? "Y" : "";
+            pw.println(nextLine + "\t" + t + "\t" + s + "\t" + h);
+        }
+
+        pw.close();
+        br.close();
+
+
+    }
+}
diff --git a/src/org/broad/igv/util/TestS3Access.java b/src/org/broad/igv/util/TestS3Access.java
new file mode 100644
index 0000000..8bfb2dc
--- /dev/null
+++ b/src/org/broad/igv/util/TestS3Access.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+
+package org.broad.igv.util;
+
+import java.io.*;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Arrays;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * Timing test for S3 storage files
+ */
+public class TestS3Access {
+
+    public static void main(String[] args) {
+        try {
+            testAsciiDownload();
+        } catch (IOException e) {
+            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+        }
+    }
+
+    static void testAsciiDownload() throws IOException {
+
+        String broadURL = "http://www.broadinstitute.org/igvdata/annotations/hg18/refGene.txt";
+        String s3URL = "http://s3.amazonaws.com/genomespace-igv/refGene.txt";
+
+        try {
+
+
+            for (int i = 0; i < 10; i++) {
+                for (String url : Arrays.asList(broadURL, s3URL)) {
+
+                    BufferedReader reader = openBufferedReader(url);
+                    int nLines = 0;
+                    long t0 = System.currentTimeMillis();
+                    while (reader.readLine() != null) {
+                        nLines++;
+                    }
+                    System.out.println(url + " read " + nLines + " in " + (System.currentTimeMillis() - t0) + " ms ");
+                    reader.close();
+                }
+            }
+        }
+        finally {
+        }
+    }
+
+    public static BufferedReader openBufferedReader(String pathOrUrl) throws IOException {
+
+        BufferedReader reader = null;
+
+        if (pathOrUrl.startsWith("http:") || pathOrUrl.startsWith("https:") ||  pathOrUrl.startsWith("file:")) {
+            URL url = new URL(pathOrUrl);
+            URLConnection connection = IGVHttpUtils.openConnection(url);
+            reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+        } else {
+            File file = new File(pathOrUrl);
+
+            FileInputStream fileInput = new FileInputStream(file);
+            if (file.getName().endsWith("gz")) {
+                GZIPInputStream in = new GZIPInputStream(fileInput);
+                reader = new BufferedReader(new InputStreamReader(in));
+            } else {
+                reader = new BufferedReader(new InputStreamReader(fileInput));
+            }
+        }
+
+        return reader;
+    }
+
+}
diff --git a/src/org/broad/igv/util/TestThreadTiming.java b/src/org/broad/igv/util/TestThreadTiming.java
index 3dff5ee..53ca2e5 100644
--- a/src/org/broad/igv/util/TestThreadTiming.java
+++ b/src/org/broad/igv/util/TestThreadTiming.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/UCSCUtils.java b/src/org/broad/igv/util/UCSCUtils.java
new file mode 100644
index 0000000..9ac0f2e
--- /dev/null
+++ b/src/org/broad/igv/util/UCSCUtils.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.util;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: May 30, 2010
+ * Time: 10:42:19 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class UCSCUtils {
+
+    static Set<String> types = new HashSet(Arrays.asList("SINE", "LINE", "LTR", "DNA", "Simple_repeat",
+            "Low_complexity", "Satellite", "RNA", "Other", "Unknown", "Uncategorized"));
+
+    public static void main(String[] args) throws IOException {
+        String ifile = "/Users/jrobinso/IGV/mm9/mm9_repmsk.txt";
+        String output = "/Users/jrobinso/IGV/mm9//repeat_masker";
+        String prefix = "mm9_repmask_";
+        splitRepeatMasker(ifile, output, prefix);
+    }
+
+    public static void splitRepeatMasker(String iFile, String outputDirectory, String prefix) throws IOException {
+
+
+        BufferedReader br = null;
+        Map<String, PrintWriter> pws = new HashMap();
+
+        try {
+            br = new BufferedReader(new FileReader(iFile));
+            File dir = new File(outputDirectory);
+            for (String type : types) {
+                File f = new File(dir, prefix + type + ".bed");
+                pws.put(type, new PrintWriter(new BufferedWriter(new FileWriter(f))));
+            }
+
+            String nextLine;
+            while ((nextLine = br.readLine()) != null) {
+                if (nextLine.startsWith("#")) continue;
+                String[] tokens = nextLine.split("\t");
+                String type = getType(tokens[5]);
+
+                // Rerrange columns for legal bed
+                pws.get(type).println(tokens[0] + "\t" + tokens[1] + "\t" + tokens[2] + "\t" +
+                        tokens[4] + "\t" + tokens[3]);
+            }
+
+
+        }
+        finally {
+            if (br != null) {
+                br.close();
+            }
+            for (PrintWriter pw : pws.values()) {
+                pw.close();
+            }
+        }
+
+    }
+
+
+    public static String getType(String s) {
+
+        s = s.replace("?", "");
+
+        if (s.contains("RNA")) {
+            return "RNA";
+        } else if (s.equals("RC")) {
+            return "Other";
+        } else if (s.equals("repClass")) {
+            return "Other";
+        } else if (types.contains(s)) {
+            return s;
+        } else {
+            return "Uncategorized";
+        }
+
+    }
+
+
+}
diff --git a/src/org/broad/igv/util/Utilities.java b/src/org/broad/igv/util/Utilities.java
index 14a9b00..e06e6b0 100644
--- a/src/org/broad/igv/util/Utilities.java
+++ b/src/org/broad/igv/util/Utilities.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
@@ -31,9 +31,10 @@ import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
-import java.util.*;
-import java.util.prefs.BackingStoreException;
-import java.util.prefs.Preferences;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.StringTokenizer;
 import java.util.zip.CRC32;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
@@ -43,94 +44,31 @@ import java.util.zip.ZipOutputStream;
  */
 public class Utilities {
 
-    private static Logger logger = Logger.getLogger(Utilities.class);
+    private static Logger log = Logger.getLogger(Utilities.class);
     final static int ZIP_ENTRY_CHUNK_SIZE = 64000;
 
-    final public static void listPreferences(Preferences preferences) throws BackingStoreException {
 
-        String keys[] = preferences.keys();
+    // TODO -- replace use of sun package
+    public static String base64Encode(String str) {
+        sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
+        byte[] bytes = str.getBytes();
+        return encoder.encode(bytes);
 
-        if (keys.length > 0) {
-
-            for (String key : keys) {
-                System.out.println(key + "=" + preferences.get(key, null));
-            }
-        }
-    }
-
-    final public static Map<String, String> getPreference(Preferences preferences)
-            throws BackingStoreException {
-
-        Map<String, String> map = new HashMap<String, String>();
-
-        String keys[] = preferences.keys();
-
-        if (keys.length > 0) {
-
-            for (String key : keys) {
-                String value = preferences.get(key, null);
-
-                if (value != null) {
-                    map.put(key, value);
-                }
-            }
-        }
-
-        return map;
     }
 
-    /**
-     * Line class used to merge feature when
-     * checking for position collisions.
-     */
-    public static class Segment {
-
-        private int start;
-        private int end;
-
-        Segment(int start, int end) {
-            this.start = start;
-            this.end = end;
-        }
-
-        public int getEnd() {
-            return end;
-        }
-
-        public void setEnd(int end) {
-            this.end = end;
-        }
-
-        public int getStart() {
-            return start;
-        }
-
-        public void setStart(int start) {
-            this.start = start;
-        }
-
-        boolean intersects(Segment line) {
-            boolean intersects = false;
-
-            if (line != null) {
-                if ((this.start >= line.start) && (this.start <= line.end)) {
-                    return true;
-                } else if ((line.start >= this.start) && (line.start <= this.end)) {
-                    return true;
-                }
-            }
-
-            return intersects;
+    // TODO -- replace use of sun package
+    public static String base64Decode(String str) {
+        try {
+            return new String((new sun.misc.BASE64Decoder()).decodeBuffer(str));
+        } catch (IOException e) {
+            log.error("Error decoding string: " + str, e);
+            return str;
         }
     }
 
-    final public static Segment getSegment(int start, int end) {
-        return new Segment(start, end);
-    }
 
     /**
      * Changes a files extension.
-     *
      */
     final public static File changeFileExtension(File file, String extension) {
 
@@ -372,9 +310,9 @@ public class Utilities {
 
             file = new File(uri.getPath());
         } catch (MalformedURLException e) {
-            logger.error(e.getMessage(), e);
+            log.error(e.getMessage(), e);
         } catch (URISyntaxException e) {
-            logger.error(e.getMessage(), e);
+            log.error(e.getMessage(), e);
         }
 
         return file;
diff --git a/src/org/broad/igv/util/ZipArchiveWrapper.java b/src/org/broad/igv/util/ZipArchiveWrapper.java
index 9bdb863..79f3c8d 100644
--- a/src/org/broad/igv/util/ZipArchiveWrapper.java
+++ b/src/org/broad/igv/util/ZipArchiveWrapper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
diff --git a/src/org/broad/igv/util/collections/DoubleArrayList.java b/src/org/broad/igv/util/collections/DoubleArrayList.java
new file mode 100644
index 0000000..e075d3f
--- /dev/null
+++ b/src/org/broad/igv/util/collections/DoubleArrayList.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.util.collections;
+
+/**
+ * Author: jrobinso
+ * Date: Jul 22, 2010
+ * <p/>
+ * ArrayList type collection for int types.  Purpose is to avoid the need to create an object for each entry
+ * in the standard java collections.
+ */
+public class DoubleArrayList {
+
+
+    private transient double[] elements;
+
+    private int size;
+
+
+    public DoubleArrayList() {
+        this(100);
+    }
+
+    public DoubleArrayList(int initialCapacity) {
+        if (initialCapacity < 0)
+            throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
+        this.elements = new double[initialCapacity];
+    }
+
+    public DoubleArrayList(double[] elements) {
+        this.elements = elements;
+        size = elements.length;
+    }
+
+    public void add(double e) {
+        if (size + 1 >= elements.length) {
+            grow();
+        }
+        elements[size++] = e;
+    }
+
+    public void addAll(double[] args) {
+        double[] newElements = new double[size + args.length];
+        System.arraycopy(elements, 0, newElements, 0, size);
+        System.arraycopy(args, 0, newElements, size, args.length);
+        elements = newElements;
+        size += args.length;
+    }
+
+    public void addAll(DoubleArrayList aList) {
+        addAll(aList.toArray());
+    }
+
+
+    public double get(int idx) {
+        return elements[idx];
+    }
+
+    public int size() {
+        return size;
+    }
+
+    public boolean isEmpty() {
+        return size == 0;
+    }
+
+    /**
+     * Empty all elements.  This logically clears the collection but does not free up any space.
+     */
+    public void clear() {
+        size = 0;
+    }
+
+    private void grow() {
+        int oldCapacity = elements.length;
+        int newCapacity;
+        if (oldCapacity < 10000000) {
+            newCapacity = oldCapacity * 2;
+        } else {
+            newCapacity = (oldCapacity * 3) / 2 + 1;
+        }
+        double[] tmp = new double[newCapacity];
+        System.arraycopy(elements, 0, tmp, 0, elements.length);
+        elements = tmp;
+    }
+
+
+    public double[] toArray() {
+        trimToSize();
+        return elements;
+    }
+
+
+    private void trimToSize() {
+        int oldCapacity = elements.length;
+        if (size < oldCapacity) {
+            double[] tmp = new double[size];
+            System.arraycopy(elements, 0, tmp, 0, size);
+            elements = tmp;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/org/broad/igv/util/collections/FloatArrayList.java b/src/org/broad/igv/util/collections/FloatArrayList.java
new file mode 100644
index 0000000..2a83208
--- /dev/null
+++ b/src/org/broad/igv/util/collections/FloatArrayList.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.util.collections;
+
+/**
+ * Author: jrobinso
+ * Date: Jul 22, 2010
+ * <p/>
+ * ArrayList type collection for int types.  Purpose is to avoid the need to create an object for each entry
+ * in the standard java collections.
+ */
+public class FloatArrayList {
+
+
+    private transient float[] elements;
+
+    private int size;
+
+
+    public FloatArrayList() {
+        this(100);
+    }
+
+    public FloatArrayList(int initialCapacity) {
+        if (initialCapacity < 0)
+            throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
+        this.elements = new float[initialCapacity];
+    }
+
+    public FloatArrayList(float[] elements) {
+        this.elements = elements;
+        size = elements.length;
+    }
+
+    public void add(float e) {
+        if (size + 1 >= elements.length) {
+            grow();
+        }
+        elements[size++] = e;
+    }
+
+    public void addAll(float[] args) {
+        float[] newElements = new float[size + args.length];
+        System.arraycopy(elements, 0, newElements, 0, size);
+        System.arraycopy(args, 0, newElements, size, args.length);
+        elements = newElements;
+        size += args.length;
+    }
+
+    public void addAll(FloatArrayList aList) {
+        addAll(aList.toArray());
+    }
+
+
+    public float get(int idx) {
+        return elements[idx];
+    }
+
+    public int size() {
+        return size;
+    }
+
+    public boolean isEmpty() {
+        return size == 0;
+    }
+
+    /**
+     * Empty all elements.  This logically clears the collection but does not free up any space.
+     */
+    public void clear() {
+        size = 0;
+    }
+
+    private void grow() {
+        int oldCapacity = elements.length;
+        int newCapacity;
+        if (oldCapacity < 10000000) {
+            newCapacity = oldCapacity * 2;
+        } else {
+            newCapacity = (oldCapacity * 3) / 2 + 1;
+        }
+        float[] tmp = new float[newCapacity];
+        System.arraycopy(elements, 0, tmp, 0, elements.length);
+        elements = tmp;
+    }
+
+
+    public float[] toArray() {
+        trimToSize();
+        return elements;
+    }
+
+
+    private void trimToSize() {
+        int oldCapacity = elements.length;
+        if (size < oldCapacity) {
+            float[] tmp = new float[size];
+            System.arraycopy(elements, 0, tmp, 0, size);
+            elements = tmp;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/org/broad/igv/util/collections/IntArrayList.java b/src/org/broad/igv/util/collections/IntArrayList.java
new file mode 100644
index 0000000..c01b640
--- /dev/null
+++ b/src/org/broad/igv/util/collections/IntArrayList.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.util.collections;
+
+/**
+ * Author: jrobinso
+ * Date: Jul 22, 2010
+ * <p/>
+ * ArrayList type collection for int types.  Purpose is to avoid the need to create an object for each entry
+ * in the standard java collections.
+ */
+public class IntArrayList {
+
+
+    private transient int[] elements;
+
+    private int size;
+
+
+    public IntArrayList() {
+        this(100);
+    }
+
+    public IntArrayList(int initialCapacity) {
+        if (initialCapacity < 0)
+            throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
+        this.elements = new int[initialCapacity];
+    }
+
+    public IntArrayList(int[] elements) {
+        this.elements = elements;
+        size = elements.length;
+    }
+
+    public void add(int e) {
+        if (size + 1 >= elements.length) {
+            grow();
+        }
+        elements[size++] = e;
+    }
+
+    public void addAll(int[] args) {
+        int[] newElements = new int[size + args.length];
+        System.arraycopy(elements, 0, newElements, 0, size);
+        System.arraycopy(args, 0, newElements, size, args.length);
+        elements = newElements;
+        size = elements.length;
+    }
+
+    public void addAll(IntArrayList aList) {
+        addAll(aList.toArray());
+    }
+
+
+    public int get(int idx) {
+        return elements[idx];
+    }
+
+    public int size() {
+        return size;
+    }
+
+    public boolean isEmpty() {
+        return size == 0;
+    }
+
+    /**
+     * Empty all elements.  This logically clears the collection but does not free up any space.
+     */
+    public void clear() {
+        size = 0;
+    }
+
+    private void grow() {
+        int oldCapacity = elements.length;
+        int newCapacity;
+        if (oldCapacity < 10000000) {
+            newCapacity = oldCapacity * 2;
+        } else {
+            newCapacity = (oldCapacity * 3) / 2 + 1;
+        }
+        int[] tmp = new int[newCapacity];
+        System.arraycopy(elements, 0, tmp, 0, elements.length);
+        elements = tmp;
+    }
+
+
+    public int[] toArray() {
+        trimToSize();
+        return elements;
+    }
+
+
+    private void trimToSize() {
+        int oldCapacity = elements.length;
+        if (size < oldCapacity) {
+            int[] tmp = new int[size];
+            System.arraycopy(elements, 0, tmp, 0, size);
+            elements = tmp;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/org/broad/igv/util/index/Interval.java b/src/org/broad/igv/util/index/Interval.java
new file mode 100644
index 0000000..fd5b9f2
--- /dev/null
+++ b/src/org/broad/igv/util/index/Interval.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.util.index;
+
+
+// Quick and dirty interval class
+public class Interval implements Comparable {
+    private final int low;
+    private final int high;
+
+    public Interval(int low, int high) {
+        assert low <= high;
+        this.low = low;
+        this.high = high;
+    }
+
+
+    public boolean equals(Object other) {
+        if (this == other)
+            return true;
+        if (this.getClass().equals(other.getClass())) {
+            Interval otherInterval = (Interval) other;
+            return (this.low == otherInterval.low &&
+                    this.high == otherInterval.high);
+        }
+        return false;
+    }
+
+
+    public int hashCode() {
+        return low;
+    }
+
+
+    public int compareTo(Object o) {
+        Interval other = (Interval) o;
+        if (this.low < other.low)
+            return -1;
+        if (this.low > other.low)
+            return 1;
+
+        if (this.high < other.high)
+            return -1;
+        if (this.high > other.high)
+            return 1;
+
+        return 0;
+    }
+
+    public String toString() {
+        return "Interval[" + this.low + ", " + this.high + "]";
+    }
+
+
+    /**
+     * Returns true if this interval overlaps the other.
+     */
+    public boolean overlaps(Interval other) {
+        return (this.low <= other.high &&
+                other.low <= this.high);
+    }
+
+
+    public int getLow() {
+        return this.low;
+    }
+
+    public int getHigh() {
+        return this.high;
+    }
+}
+
diff --git a/src/org/broad/igv/util/index/IntervalTree.java b/src/org/broad/igv/util/index/IntervalTree.java
new file mode 100644
index 0000000..1101527
--- /dev/null
+++ b/src/org/broad/igv/util/index/IntervalTree.java
@@ -0,0 +1,528 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.igv.util.index;
+
+
+/** An implementation of an interval tree, following the explanation.
+ * from CLR.
+ */
+
+
+import org.apache.log4j.Logger;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class IntervalTree {
+
+    private static Logger logger = Logger.getLogger(IntervalTree.class);
+    boolean immutable = false;
+    Node root;
+    Node NIL = Node.NIL;
+
+    public IntervalTree() {
+        this.root = NIL;
+    }
+
+    public IntervalTree(boolean immutable) {
+        this.immutable = immutable;
+        this.root = NIL;
+    }
+
+    public void insert(Interval interval) {
+        if (immutable) {
+            throw new java.lang.UnsupportedOperationException("Tree is immutable.  Inserts not allowed");
+        }
+        Node node = new Node(interval);
+        insert(node);
+    }
+
+
+    // Returns all matches as a list of Intervals
+    public List<Interval> findOverlapping(Interval interval) {
+        logger.debug("Starting search for " + interval);
+
+        if (root().isNull()) {
+            return Collections.emptyList();
+        }
+
+        List<Interval> results = new ArrayList();
+        searchAll(interval, root(), results);
+        return results;
+    }
+
+    public String toString() {
+        return root().toString();
+    }
+
+    private List<Interval> searchAll(Interval interval, Node node, List<Interval> results) {
+
+        System.out.println("Visiting " + node.interval);
+
+        if (node.interval.overlaps(interval)) {
+            results.add(node.interval);
+        }
+
+        if (!node.left.isNull() && node.left.max >= interval.getLow()) {
+            searchAll(interval, node.left, results);
+        }
+
+        if (!node.right.isNull() && node.right.min <= interval.getHigh()) {
+            searchAll(interval, node.right, results);
+        }
+
+        return results;
+    }
+
+
+    /**
+     * Used for testing only.
+     *
+     * @param node
+     * @return
+     */
+    private int getRealMax(Node node) {
+        if (node.isNull())
+            return Integer.MIN_VALUE;
+        int leftMax = getRealMax(node.left);
+        int rightMax = getRealMax(node.right);
+        int nodeHigh = (node.interval).getHigh();
+
+        int max1 = (leftMax > rightMax ? leftMax : rightMax);
+        return (max1 > nodeHigh ? max1 : nodeHigh);
+    }
+
+    /**
+     * Used for testing only
+     *
+     * @param node
+     * @return
+     */
+    private int getRealMin(Node node) {
+        if (node.isNull())
+            return Integer.MAX_VALUE;
+
+        int leftMin = getRealMin(node.left);
+        int rightMin = getRealMin(node.right);
+        int nodeLow = (node.interval).getLow();
+
+        int min1 = (leftMin < rightMin ? leftMin : rightMin);
+        return (min1 < nodeLow ? min1 : nodeLow);
+    }
+
+
+    private void insert(Node x) {
+        assert (x != null);
+        assert (!x.isNull());
+
+        treeInsert(x);
+        x.color = Node.RED;
+        while (x != this.root && x.parent.color == Node.RED) {
+            if (x.parent == x.parent.parent.left) {
+                Node y = x.parent.parent.right;
+                if (y.color == Node.RED) {
+                    x.parent.color = Node.BLACK;
+                    y.color = Node.BLACK;
+                    x.parent.parent.color = Node.RED;
+                    x = x.parent.parent;
+                } else {
+                    if (x == x.parent.right) {
+                        x = x.parent;
+                        this.leftRotate(x);
+                    }
+                    x.parent.color = Node.BLACK;
+                    x.parent.parent.color = Node.RED;
+                    this.rightRotate(x.parent.parent);
+                }
+            } else {
+                Node y = x.parent.parent.left;
+                if (y.color == Node.RED) {
+                    x.parent.color = Node.BLACK;
+                    y.color = Node.BLACK;
+                    x.parent.parent.color = Node.RED;
+                    x = x.parent.parent;
+                } else {
+                    if (x == x.parent.left) {
+                        x = x.parent;
+                        this.rightRotate(x);
+                    }
+                    x.parent.color = Node.BLACK;
+                    x.parent.parent.color = Node.RED;
+                    this.leftRotate(x.parent.parent);
+                }
+            }
+        }
+        this.root.color = Node.BLACK;
+    }
+
+
+    private Node root() {
+        return this.root;
+    }
+
+
+    private Node minimum(Node node) {
+        assert (node != null);
+        assert (!node.isNull());
+        while (!node.left.isNull()) {
+            node = node.left;
+        }
+        return node;
+    }
+
+
+    private Node maximum(Node node) {
+        assert (node != null);
+        assert (!node.isNull());
+        while (!node.right.isNull()) {
+            node = node.right;
+        }
+        return node;
+    }
+
+
+    private Node successor(Node x) {
+        assert (x != null);
+        assert (!x.isNull());
+        if (!x.right.isNull()) {
+            return this.minimum(x.right);
+        }
+        Node y = x.parent;
+        while ((!y.isNull()) && x == y.right) {
+            x = y;
+            y = y.parent;
+        }
+        return y;
+    }
+
+
+    private Node predecessor(Node x) {
+        assert (x != null);
+        assert (!x.isNull());
+
+        if (!x.left.isNull()) {
+            return this.maximum(x.left);
+        }
+        Node y = x.parent;
+        while ((!y.isNull()) && x == y.left) {
+            x = y;
+            y = y.parent;
+        }
+        return y;
+    }
+
+
+    private void leftRotate(Node x) {
+        Node y = x.right;
+        x.right = y.left;
+        if (y.left != NIL) {
+            y.left.parent = x;
+        }
+        y.parent = x.parent;
+        if (x.parent == NIL) {
+            this.root = y;
+        } else {
+            if (x.parent.left == x) {
+                x.parent.left = y;
+            } else {
+                x.parent.right = y;
+            }
+        }
+        y.left = x;
+        x.parent = y;
+
+        applyUpdate(x);
+        // no need to apply update on y, since it'll y is an ancestor
+        // of x, and will be touched by applyUpdate().
+    }
+
+
+    private void rightRotate(Node x) {
+        Node y = x.left;
+        x.left = y.right;
+        if (y.right != NIL) {
+            y.right.parent = x;
+        }
+        y.parent = x.parent;
+        if (x.parent == NIL) {
+            this.root = y;
+        } else {
+            if (x.parent.right == x) {
+                x.parent.right = y;
+            } else {
+                x.parent.left = y;
+            }
+        }
+        y.right = x;
+        x.parent = y;
+
+
+        applyUpdate(x);
+        // no need to apply update on y, since it'll y is an ancestor
+        // of x, and will be touched by applyUpdate().
+    }
+
+
+    /**
+     * Note:  Does not maintain RB constraints,  this is done post insert
+     *
+     * @param x
+     */
+    private void treeInsert(Node x) {
+        Node node = this.root;
+        Node y = NIL;
+        while (node != NIL) {
+            y = node;
+            if (x.interval.getLow() <= node.interval.getLow()) {
+                node = node.left;
+            } else {
+                node = node.right;
+            }
+        }
+        x.parent = y;
+
+        if (y == NIL) {
+            this.root = x;
+            x.left = x.right = NIL;
+        } else {
+            if (x.interval.getLow() <= y.interval.getLow()) {
+                y.left = x;
+            } else {
+                y.right = x;
+            }
+        }
+
+        this.applyUpdate(x);
+    }
+
+
+    // Applies the statistic update on the node and its ancestors.
+    private void applyUpdate(Node node) {
+        while (!node.isNull()) {
+            this.update(node);
+            node = node.parent;
+        }
+    }
+
+    private void update(Node node) {
+        node.max = Math.max(Math.max(node.left.max, node.right.max), node.interval.getHigh());
+        node.min = Math.min(Math.min(node.left.min, node.right.min), node.interval.getLow());
+    }
+
+
+    /**
+     * Returns the number of nodes in the tree.
+     */
+    public int size() {
+        return _size(this.root);
+    }
+
+
+    private int _size(Node node) {
+        if (node.isNull())
+            return 0;
+        return 1 + _size(node.left) + _size(node.right);
+    }
+
+
+    private boolean allRedNodesFollowConstraints(Node node) {
+        if (node.isNull())
+            return true;
+
+        if (node.color == Node.BLACK) {
+            return (allRedNodesFollowConstraints(node.left) &&
+                    allRedNodesFollowConstraints(node.right));
+        }
+
+        // At this point, we know we're on a RED node.
+        return (node.left.color == Node.BLACK &&
+                node.right.color == Node.BLACK &&
+                allRedNodesFollowConstraints(node.left) &&
+                allRedNodesFollowConstraints(node.right));
+    }
+
+
+    // Check that both ends are equally balanced in terms of black height.
+    private boolean isBalancedBlackHeight(Node node) {
+        if (node.isNull())
+            return true;
+        return (blackHeight(node.left) == blackHeight(node.right) &&
+                isBalancedBlackHeight(node.left) &&
+                isBalancedBlackHeight(node.right));
+    }
+
+
+    // The black height of a node should be left/right equal.
+    private int blackHeight(Node node) {
+        if (node.isNull())
+            return 0;
+        int leftBlackHeight = blackHeight(node.left);
+        if (node.color == Node.BLACK) {
+            return leftBlackHeight + 1;
+        } else {
+            return leftBlackHeight;
+        }
+    }
+
+
+    /**
+     * Test code: make sure that the tree has all the properties
+     * defined by Red Black trees and interval trees
+     * <p/>
+     * o.  Root is black.
+     * <p/>
+     * o.  NIL is black.
+     * <p/>
+     * o.  Red nodes have black children.
+     * <p/>
+     * o.  Every path from root to leaves contains the same number of
+     * black nodes.
+     * <p/>
+     * o.  getMax(node) is the maximum of any interval rooted at that node..
+     * <p/>
+     * This code is expensive, and only meant to be used for
+     * assertions and testing.
+     */
+
+    public boolean isValid() {
+        if (this.root.color != Node.BLACK) {
+            logger.warn("root color is wrong");
+            return false;
+        }
+        if (NIL.color != Node.BLACK) {
+            logger.warn("NIL color is wrong");
+            return false;
+        }
+        if (allRedNodesFollowConstraints(this.root) == false) {
+            logger.warn("red node doesn't follow constraints");
+            return false;
+        }
+        if (isBalancedBlackHeight(this.root) == false) {
+            logger.warn("black height unbalanced");
+            return false;
+        }
+
+        return hasCorrectMaxFields(this.root) &&
+                hasCorrectMinFields(this.root);
+    }
+
+
+    private boolean hasCorrectMaxFields(Node node) {
+        if (node.isNull())
+            return true;
+        return (getRealMax(node) == (node.max) &&
+                hasCorrectMaxFields(node.left) &&
+                hasCorrectMaxFields(node.right));
+    }
+
+
+    private boolean hasCorrectMinFields(Node node) {
+        if (node.isNull())
+            return true;
+        return (getRealMin(node) == (node.min) &&
+                hasCorrectMinFields(node.left) &&
+                hasCorrectMinFields(node.right));
+    }
+
+
+    static class Node {
+
+        public static boolean BLACK = false;
+        public static boolean RED = true;
+
+        Interval interval;
+        int min;
+        int max;
+        Node left;
+        Node right;
+
+        // Color and parent are used for inserts.  If tree is immutable these are not required (no requirement
+        // to store these persistently).
+        boolean color;
+        Node parent;
+
+
+        private Node() {
+            this.max = Integer.MIN_VALUE;
+            this.min = Integer.MAX_VALUE;
+        }
+
+        public void store(DataOutputStream dos) throws IOException {
+            dos.writeInt(interval.getLow());
+            dos.writeInt(interval.getHigh());
+            dos.writeInt(min);
+            dos.writeInt(max);
+
+        }
+
+        public Node(Interval interval) {
+            this();
+            this.parent = NIL;
+            this.left = NIL;
+            this.right = NIL;
+            this.interval = interval;
+            this.color = RED;
+        }
+
+
+        static Node NIL;
+
+        static {
+            NIL = new Node();
+            NIL.color = BLACK;
+            NIL.parent = NIL;
+            NIL.left = NIL;
+            NIL.right = NIL;
+        }
+
+
+        public boolean isNull() {
+            return this == NIL;
+        }
+
+
+        public String toString() {
+            if (this == NIL) {
+                return "nil";
+            }
+            /* return
+                    "(" + this.interval + " " + (this.color == RED ? "RED" : "BLACK") +
+                            " (" + this.left.toString() + ", " + this.right.toString() + ")";
+                            */
+            StringBuffer buf = new StringBuffer();
+            _toString(buf);
+            return buf.toString();
+        }
+
+        public void _toString(StringBuffer buf) {
+            if (this == NIL) {
+                buf.append("nil");
+                return;
+            }
+            buf.append(this.interval + " -> " + this.left.interval + ", " + this.right.interval);
+            buf.append("\n");
+            this.left._toString(buf);
+            this.right._toString(buf);
+        }
+    }
+}
+
diff --git a/src/org/broad/tribble/AbstractFeatureCodec.java b/src/org/broad/tribble/AbstractFeatureCodec.java
new file mode 100644
index 0000000..732985c
--- /dev/null
+++ b/src/org/broad/tribble/AbstractFeatureCodec.java
@@ -0,0 +1,18 @@
+package org.broad.tribble;
+
+import org.broad.tribble.util.LineReader;
+
+import java.io.IOException;
+import java.io.File;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: Dec 22, 2009
+ * Time: 9:12:43 AM
+ * To change this template use File | Settings | File Templates.
+ */
+public abstract class AbstractFeatureCodec<T extends Feature> implements FeatureCodec {
+
+
+}
diff --git a/src/org/broad/tribble/Feature.java b/src/org/broad/tribble/Feature.java
new file mode 100644
index 0000000..de5b923
--- /dev/null
+++ b/src/org/broad/tribble/Feature.java
@@ -0,0 +1,26 @@
+package org.broad.tribble;
+
+
+/**
+ * Represents a locus on a reference sequence.   The coordiante conventions are 1-based fully closed.  For example,
+ * a features  spanning the second and third base of a sequence would have coordinates start = 2,  end = 3,  and
+ * a length of 2.
+ */
+public interface Feature {
+
+    /**
+     * Return the features reference sequence name, e.g chromosome or contig
+     */
+    public String getChr();
+
+    /**
+     * Return the start position in 1-based coordinates (first base is 1)
+     */
+    public int getStart();
+
+    /**
+     * Return the end position following 1-based fully closed conventions.  The length of a feature is
+     * end - start + 1;
+     */
+    public int getEnd();
+}
diff --git a/src/org/broad/tribble/FeatureCodec.java b/src/org/broad/tribble/FeatureCodec.java
new file mode 100644
index 0000000..aebf4e9
--- /dev/null
+++ b/src/org/broad/tribble/FeatureCodec.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble;
+
+import org.broad.tribble.util.LineReader;
+
+import java.lang.reflect.Type;
+
+/**
+ * the base interface for classes that read in features.
+ * @param <T> The feature type this codec reads
+ */
+public interface FeatureCodec<T extends Feature> {
+    /**
+     * Decode a line to obtain just its FeatureLoc for indexing -- contig, start, and stop.
+     *
+     * @param line the input line to decode
+     * @return  Return the FeatureLoc encoded by the line, or null if the line does not represent a feature (e.g. is
+     * a comment)
+     */
+    public Feature decodeLoc(String line);
+
+    /**
+     * Decode a line as a Feature.
+     *
+     * @param line the input line to decode
+     * @return  Return the Feature encoded by the line,  or null if the line does not represent a feature (e.g. is
+     * a comment)
+     */
+    public T decode(String line);
+
+    /**
+     * This function returns the object the codec generates.  This is allowed to be Feature in the case where
+     * conditionally different types are generated.  Be as specific as you can though.
+     *
+     * This function is used by reflections based tools, so we can know the underlying type
+     *
+     * @return the feature type this codec generates.
+     */
+    public Class<T> getFeatureType();
+
+
+    /**  Read and return the header, or null if there is no header.
+     *
+     * @return header object
+     */
+    public Object readHeader(LineReader reader);
+}
diff --git a/src/org/broad/tribble/FeatureIterator.java b/src/org/broad/tribble/FeatureIterator.java
new file mode 100644
index 0000000..dfcf3f5
--- /dev/null
+++ b/src/org/broad/tribble/FeatureIterator.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2009-2010 by The Broad Institute, Inc.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble;
+
+import java.util.Iterator;
+
+/**
+ * @Author Aaron
+ *
+ * the base iterator type for feature iterators.  We support the close method for
+ * feature readers that pass a new instance of a stream to the iterator (which has
+ * to be closed).
+ */
+public interface FeatureIterator<T> extends Iterator<T>, Iterable<T> {
+    // close the any underlying dependencies.
+    public void close();
+}
diff --git a/src/org/broad/tribble/FeatureReader.java b/src/org/broad/tribble/FeatureReader.java
new file mode 100644
index 0000000..b4595d2
--- /dev/null
+++ b/src/org/broad/tribble/FeatureReader.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble;
+
+import org.broad.tribble.util.CloseableTribbleIterator;
+
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * the basic interface that feature readers need to match
+ * @param <T> a feature type
+ */
+public interface FeatureReader<T extends Feature> {
+    
+    public CloseableTribbleIterator<T> query(final String chr, final int start, final int end) throws IOException;
+
+    public CloseableTribbleIterator<T> query(final String chr, final int start, final int end, final boolean contained) throws IOException;
+
+    public CloseableTribbleIterator<T> iterator() throws IOException;
+
+    public void close() throws IOException;
+
+    public Set<String> getSequenceNames();
+    
+}
diff --git a/src/org/broad/tribble/exception/CodecLineParsingException.java b/src/org/broad/tribble/exception/CodecLineParsingException.java
new file mode 100644
index 0000000..4fe2879
--- /dev/null
+++ b/src/org/broad/tribble/exception/CodecLineParsingException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble.exception;
+
+
+/**
+ *
+ * @author aaron
+ *
+ * Class CodecLineParsingException
+ *
+ * a generic exception we use if the codec has trouble parsing the line its given
+ */
+public class CodecLineParsingException extends RuntimeException {
+
+    public CodecLineParsingException(Throwable cause) {
+        super(cause);
+    }
+
+    public CodecLineParsingException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public CodecLineParsingException(String message) {
+        super(message);
+    }
+
+    public CodecLineParsingException() {
+    }
+}
\ No newline at end of file
diff --git a/src/org/broad/tribble/exception/UnsortedFileException.java b/src/org/broad/tribble/exception/UnsortedFileException.java
new file mode 100644
index 0000000..0b01016
--- /dev/null
+++ b/src/org/broad/tribble/exception/UnsortedFileException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble.exception;
+
+public class UnsortedFileException extends RuntimeException {
+
+    public UnsortedFileException(Throwable cause) {
+        super(cause);
+    }
+
+    public UnsortedFileException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public UnsortedFileException(String message) {
+        super(message);
+    }
+
+    public UnsortedFileException() {
+    }
+}
\ No newline at end of file
diff --git a/src/org/broad/tribble/index/AbstractIndex.java b/src/org/broad/tribble/index/AbstractIndex.java
new file mode 100644
index 0000000..5d28640
--- /dev/null
+++ b/src/org/broad/tribble/index/AbstractIndex.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble.index;
+
+import org.apache.log4j.Logger;
+import org.broad.tribble.util.LittleEndianInputStream;
+import org.broad.tribble.util.LittleEndianOutputStream;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: Jul 9, 2010
+ * Time: 8:37:26 AM
+ * To change this template use File | Settings | File Templates.
+ */
+public abstract class AbstractIndex implements Index {
+    private static Logger log = Logger.getLogger(AbstractIndex.class);
+
+    public static int VERSION = 3;
+
+
+    private int version;
+    String indexedFile;
+    long indexedFileSize;
+    long indexedFileTS;
+    String indexedFileMD5 = "";
+    int flags;
+    Map<String, String> properties;
+    protected LinkedHashMap<String, ChrIndex> chrIndeces;
+    private static final int SEQUENCE_DICTIONARY_FLAG = 0x8000;
+
+
+    public AbstractIndex() {
+        this.version = VERSION;
+        this.properties = new HashMap();
+        chrIndeces = new LinkedHashMap();
+    }
+
+    public AbstractIndex(String featureFile) {
+        this.version = VERSION; // <= is overriden when file is read
+        this.properties = new HashMap();
+        chrIndeces = new LinkedHashMap();
+        this.indexedFile = featureFile;
+        this.indexedFileSize = featureFile.length();
+        // TODO -- get time stamp
+        //this.indexedFileTS = featureFile.lastModified();
+    }
+
+
+    /**
+     * check the current version against the version we read in
+     *
+     * @return true if we're up to date, false otherwise
+     */
+    public boolean isCurrentVersion() {
+        return version == VERSION;
+    }
+
+    public void setMD5(String md5) {
+        this.indexedFileMD5 = md5;
+    }
+
+    public boolean containsChromosome(String chr) {
+        return chrIndeces.containsKey(chr);
+    }
+
+    protected abstract int getType();
+
+
+    private void writeHeader(LittleEndianOutputStream dos) throws IOException {
+
+        int magicNumber = 1480870228;   //  byte[]{'T', 'I', 'D', 'X'};
+
+        dos.writeInt(magicNumber);
+        dos.writeInt(getType());
+        dos.writeInt(version);
+        dos.writeString(indexedFile);
+        dos.writeLong(indexedFileSize);
+        dos.writeLong(indexedFileTS);
+        dos.writeString(indexedFileMD5);
+        dos.writeInt(flags);
+
+        // Properties (Version 3 and later)
+        dos.writeInt(properties.size());
+        for (Map.Entry<String, String> prop : properties.entrySet()) {
+            dos.writeString(prop.getKey());
+            dos.writeString(prop.getValue());
+        }
+    }
+
+    private void readHeader(LittleEndianInputStream dis) throws IOException {
+
+        version = dis.readInt();
+        indexedFile = dis.readString();
+        indexedFileSize = dis.readLong();
+        indexedFileTS = dis.readLong();
+        indexedFileMD5 = dis.readString();
+        flags = dis.readInt();
+        if (version < 3 && (flags & SEQUENCE_DICTIONARY_FLAG) == SEQUENCE_DICTIONARY_FLAG) {
+            readSequenceDictionary(dis);
+        }
+
+        if (version >= 3) {
+            int nProperties = dis.readInt();
+            while (nProperties-- > 0) {
+                String key = dis.readString();
+                String value = dis.readString();
+                properties.put(key, value);
+            }
+        }
+    }
+
+
+    /**
+     * Kept to maintain backward compatibility with pre version 3 indexes.  The sequence dictionary is no longer
+     * used,  use getSequenceNames() instead.
+     *
+     * @param dis
+     * @throws IOException
+     */
+    private void readSequenceDictionary(LittleEndianInputStream dis) throws IOException {
+        int size = dis.readInt();
+        if (size < 0) throw new IllegalStateException("Size of the sequence dictionary entries is negitive");
+        for (int x = 0; x < size; x++) {
+            dis.readString();
+            dis.readInt();
+        }
+    }
+
+    public LinkedHashSet<String> getSequenceNames() {
+        return new LinkedHashSet(chrIndeces.keySet());
+    }
+
+    /**
+     * @param chr
+     * @param start
+     * @param end
+     * @return
+     */
+    public List<Block> getBlocks(String chr, int start, int end) {
+
+        ChrIndex chrIdx = chrIndeces.get(chr);
+        if (chrIdx == null) {
+
+            // Todo -- throw an exception ?
+            return null;
+        } else {
+            return chrIdx.getBlocks(start, end);
+        }
+    }
+
+    public abstract Class getIndexClass();
+
+    /**
+     * Store self to a file
+     *
+     * @param stream the input stream
+     * @throws java.io.IOException
+     */
+    public void write(LittleEndianOutputStream stream) throws IOException {
+        writeHeader(stream);
+
+        //# of chromosomes
+        stream.writeInt(chrIndeces.size());
+        for (ChrIndex chrIdx : chrIndeces.values()) {
+            chrIdx.write(stream);
+        }
+    }
+
+
+    public void read(LittleEndianInputStream dis) throws IOException {
+
+        try {
+            readHeader(dis);
+
+            int nChromosomes = dis.readInt();
+            chrIndeces = new LinkedHashMap<String, ChrIndex>(nChromosomes * 2);
+
+            while (nChromosomes-- > 0) {
+                ChrIndex chrIdx = (ChrIndex) getIndexClass().newInstance();
+                chrIdx.read(dis);
+                chrIndeces.put(chrIdx.getName(), chrIdx);
+            }
+
+        } catch (InstantiationException e) {
+            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+        } finally {
+            dis.close();
+        }
+
+    }
+}
diff --git a/src/org/broad/tribble/index/Block.java b/src/org/broad/tribble/index/Block.java
new file mode 100644
index 0000000..0eccf3e
--- /dev/null
+++ b/src/org/broad/tribble/index/Block.java
@@ -0,0 +1,48 @@
+package org.broad.tribble.index;
+
+/**
+ * Represents a contiguous block of bytes in a file, defined by a start position and size (in bytes)
+*/
+public class Block {
+
+    private long startPosition;
+    private int size;
+
+    /**
+     * Constructs ...
+     *
+     * @param startPosition
+     * @param size
+     */
+    public Block(long startPosition, int size) {
+        this.startPosition = startPosition;
+        this.size = size;
+    }
+
+    /**
+     * @return the startPosition
+     */
+    public long getStartPosition() {
+        return startPosition;
+    }
+
+    public long getEndPosition() {
+        return startPosition + size;
+    }
+
+    /**
+     * This method is used to aid in consolidating blocks
+     */
+    public void setEndPosition(long endPosition) {
+        assert(endPosition > startPosition);
+        size = (int) (endPosition - startPosition);
+
+    }
+
+    /**
+     * @return the # of bytes in this block
+     */
+    public int getSize() {
+        return size;
+    }
+}
diff --git a/src/org/broad/tribble/index/ChrIndex.java b/src/org/broad/tribble/index/ChrIndex.java
new file mode 100644
index 0000000..3565e70
--- /dev/null
+++ b/src/org/broad/tribble/index/ChrIndex.java
@@ -0,0 +1,22 @@
+package org.broad.tribble.index;
+
+import org.broad.tribble.util.LittleEndianInputStream;
+import org.broad.tribble.util.LittleEndianOutputStream;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Represents an index on a specific chromosome
+ */
+
+public interface ChrIndex {
+
+    public String getName();
+    
+    List<Block> getBlocks(int start, int end);
+
+    void write(LittleEndianOutputStream dos) throws IOException;
+
+    void read(LittleEndianInputStream dis) throws IOException;
+}
diff --git a/src/org/broad/tribble/index/Index.java b/src/org/broad/tribble/index/Index.java
new file mode 100644
index 0000000..0ac6ebb
--- /dev/null
+++ b/src/org/broad/tribble/index/Index.java
@@ -0,0 +1,48 @@
+package org.broad.tribble.index;
+
+import org.broad.tribble.util.LittleEndianInputStream;
+import org.broad.tribble.util.LittleEndianOutputStream;
+
+import java.io.IOException;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+/**
+ * Interface for all index implementations.
+ */
+public interface Index {
+    /**
+     *
+     * @param chr the chromosome
+     * @param start the start position
+     * @param end the end position
+     * @return a list of blocks that contain the specified interval
+     */
+    List<Block> getBlocks(String chr, int start, int end);
+
+    /**
+     * does this index represent an up-to-date version
+     * @return true if the index is up to date, false otherwise
+     */
+    public boolean isCurrentVersion();
+
+    /**
+     * get a list of the sequence names we've seen during indexing, in order
+     * @return a LinkedHashSet, which guarantees the ordering
+     */
+    LinkedHashSet<String> getSequenceNames();
+
+    /**
+     * read in the index
+     * @param stream an input stream to read from
+     * @throws IOException if we have problems reading the index from the stream
+     */
+    public void read(LittleEndianInputStream stream)  throws IOException;
+
+    /**
+     * all indexes are writable to disk
+     * @param stream the stream to write the index to
+     * @throws IOException if the index is unable to write to the specified location
+     */
+    public void write(LittleEndianOutputStream stream) throws IOException;
+}
diff --git a/src/org/broad/tribble/index/IndexCreator.java b/src/org/broad/tribble/index/IndexCreator.java
new file mode 100644
index 0000000..d017389
--- /dev/null
+++ b/src/org/broad/tribble/index/IndexCreator.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble.index;
+
+import org.broad.tribble.FeatureCodec;
+import org.broad.tribble.util.AsciiLineReader;
+import org.broad.tribble.util.ParsingUtils;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: Jul 16, 2010
+ * Time: 10:02:27 PM
+ * To change this template use File | Settings | File Templates.
+ */
+abstract public class IndexCreator {
+    protected File featureFile;
+    protected FeatureCodec codec;
+    protected File idxFile;
+
+    public IndexCreator(File featureFile, FeatureCodec codec) {
+        this.featureFile = featureFile;
+        this.codec = codec;
+        idxFile = new File(featureFile.getParent(), featureFile.getName() + ".idx");
+        forceReadHeader(featureFile);
+    }
+
+    protected void forceReadHeader(File featureFile) {
+
+        AsciiLineReader reader = null;
+        try {
+            reader = ParsingUtils.openAsciiReader(featureFile.getAbsolutePath());
+            codec.readHeader(reader);
+        }
+        catch (IOException e) {
+            //TODO -- fix this with proper exception handling
+            e.printStackTrace();
+        } finally {
+            if (reader != null) {
+                reader.close();
+            }
+        }
+    }
+
+    public abstract Index createIndex() throws IOException ;
+}
diff --git a/src/org/broad/tribble/index/IndexFactory.java b/src/org/broad/tribble/index/IndexFactory.java
new file mode 100644
index 0000000..b4befe6
--- /dev/null
+++ b/src/org/broad/tribble/index/IndexFactory.java
@@ -0,0 +1,81 @@
+package org.broad.tribble.index;
+
+import org.apache.log4j.Logger;
+import org.broad.tribble.index.interval.IntervalTreeIndex;
+import org.broad.tribble.index.linear.LinearIndex;
+import org.broad.tribble.util.LittleEndianInputStream;
+import org.broad.tribble.util.SeekableStreamFactory;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Factory class for creating indexes.  It is the responsibility of this class to determine and create the
+ * correct index type from the input file or stream
+ */
+
+public class IndexFactory {
+
+    private static Logger log = Logger.getLogger(IndexFactory.class);
+
+    final public static int LINEAR = 1;
+    final public static int INTERVAL_TREE = 2;
+
+    /**
+     * Load in index from the specified file.   The type of index (LinearIndex or IntervalTreeIndex) is determined
+     * at run time by reading the type flag in the file.
+     *
+     * @param indexFile
+     * @return
+     */
+    public static Index loadIndex(String indexFile) {
+        Index idx = null;
+        InputStream is = null;
+        try {
+            is = SeekableStreamFactory.getStreamFor(indexFile);
+            LittleEndianInputStream dis = new LittleEndianInputStream(new BufferedInputStream(is));
+
+            // Read the type and version,  then create the appropriate type
+            int magicNumber = dis.readInt();
+            int type = dis.readInt();
+            Class indexClass = getIndexClass(type);
+
+            idx = (Index) indexClass.newInstance();
+            idx.read(dis);
+
+
+        } catch (Exception ex) {
+            log.error("Error reading index file: " + indexFile, ex);
+            // TODO -- throw application exception ?
+            throw new RuntimeException(ex);
+        }
+        finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    log.error("Error closing indexFile: " + indexFile, e);
+                }
+            }
+        }
+        return idx;
+    }
+
+
+    /**
+     * @param type -- type flag
+     * @return Return the index class for the type.
+     */
+    private static Class getIndexClass(int type) {
+        switch (type) {
+            case LINEAR:
+                return LinearIndex.class;
+            case INTERVAL_TREE:
+                return IntervalTreeIndex.class;
+            default:
+                throw new RuntimeException("Unrecognized type flag in index file");
+        }
+    }
+
+}
diff --git a/src/org/broad/tribble/index/interval/Interval.java b/src/org/broad/tribble/index/interval/Interval.java
new file mode 100644
index 0000000..b9830a0
--- /dev/null
+++ b/src/org/broad/tribble/index/interval/Interval.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble.index.interval;
+
+import org.broad.tribble.index.Block;
+
+
+// Quick and dirty interval class
+
+public class Interval implements Comparable {
+    /**
+     * Start of the interval in genomic coordinates
+     */
+     final int start;
+
+    /**
+     * End of the interval in genomic coordinates
+     */
+     final int end;
+
+    /**
+     * File block  (position, size) containing the data for this interval
+     */
+    private Block block;
+
+    public Interval(int start, int end) {
+        assert start <= end;
+        this.start = start;
+        this.end = end;
+    }
+
+
+    public Interval(int start, int end, Block block) {
+        assert start <= end;
+        this.start = start;
+        this.end = end;
+        this.block = block;
+    }
+
+
+    public boolean equals(Object other) {
+        if (this == other)
+            return true;
+        if (this.getClass().equals(other.getClass())) {
+            Interval otherInterval = (Interval) other;
+            return (this.start == otherInterval.start &&
+                    this.end == otherInterval.end);
+        }
+        return false;
+    }
+
+
+    public int hashCode() {
+        return start;
+    }
+
+
+    public int compareTo(Object o) {
+        Interval other = (Interval) o;
+        if (this.start < other.start)
+            return -1;
+        if (this.start > other.start)
+            return 1;
+
+        if (this.end < other.end)
+            return -1;
+        if (this.end > other.end)
+            return 1;
+
+        return 0;
+    }
+
+    public String toString() {
+        return "Interval[" + this.start + ", " + this.end + "]";
+    }
+
+
+    /**
+     * Returns true if this interval overlaps the other.
+     */
+    public boolean overlaps(Interval other) {
+        return (this.start <= other.end &&
+                other.start <= this.end);
+    }
+
+    /**
+     * File position / # of bytes for this interval
+     */
+    public Block getBlock() {
+        return block;
+    }
+}
+
diff --git a/src/org/broad/tribble/index/interval/IntervalIndexCreator.java b/src/org/broad/tribble/index/interval/IntervalIndexCreator.java
new file mode 100644
index 0000000..0deb958
--- /dev/null
+++ b/src/org/broad/tribble/index/interval/IntervalIndexCreator.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble.index.interval;
+
+import org.apache.log4j.Logger;
+import org.broad.tribble.Feature;
+import org.broad.tribble.FeatureCodec;
+import org.broad.tribble.exception.UnsortedFileException;
+import org.broad.tribble.index.Block;
+import org.broad.tribble.index.Index;
+import org.broad.tribble.index.IndexCreator;
+import org.broad.tribble.util.AsciiLineReader;
+import org.broad.tribble.util.LittleEndianOutputStream;
+
+import java.io.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: Jul 12, 2010
+ * Time: 12:59:47 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class IntervalIndexCreator extends IndexCreator {
+
+    private static Logger log = Logger.getLogger(IntervalIndexCreator.class);
+
+    static int DEFAULT_FEATURE_COUNT = 1000;
+
+    private int featuresPerInterval;
+
+    private boolean verbose = false;
+
+    /**
+     * Constructor.
+     *
+     * @param featureFile
+     * @param codec
+     */
+    public IntervalIndexCreator(File featureFile, FeatureCodec codec) {
+        super(featureFile, codec);
+        this.featuresPerInterval = DEFAULT_FEATURE_COUNT;
+
+    }
+
+    public void setFeaturesPerInterval(int featuresPerInterval) {
+        this.featuresPerInterval = featuresPerInterval;
+    }
+
+    public Index createIndex() throws IOException {
+
+        //int nHeaderLines = getHeaderLineCount();
+        IntervalTreeIndex featureIndex = new IntervalTreeIndex(featureFile.getAbsolutePath());
+
+        FileInputStream is = new FileInputStream(featureFile);
+        AsciiLineReader reader = new AsciiLineReader(is);
+
+        // make sure to read the header off first
+        codec.readHeader(reader);
+
+        long fileLength = featureFile.length();
+        long progressIncrement = fileLength / 100;
+
+        long filePosition;
+        long currentFilePosition = 0;
+        String lastChr = null;
+        int lastStart = 0;
+        int intervalStart = 0;
+        int intervalEnd = 0;
+        int progressCounter = 1; // progress in %
+        int lineNumber = 0;
+        String nextLine = "";
+        int featureCount = 0;
+
+        // Skip to first feature
+        Feature f = null;
+        do {
+            lineNumber++;
+            filePosition = reader.getPosition();
+            nextLine = reader.readLine();
+        } while (nextLine != null && (f = codec.decodeLoc(nextLine)) == null);
+
+        // "f" should never be null here.
+        if (f != null) {
+            lastChr = f.getChr();
+            intervalStart = f.getStart();
+            intervalEnd = f.getEnd();
+            featureCount++;
+        }
+
+        while ((nextLine = reader.readLine()) != null) {
+            lineNumber++;
+
+            f = codec.decodeLoc(nextLine);
+            if (f == null) {
+                continue;
+            }
+
+            String chr = f.getChr();
+            featureCount++;
+
+            if (!chr.equals(lastChr) || featureCount >= featuresPerInterval) {
+                // Record interval
+                int bytesCount = (int) (currentFilePosition - filePosition);
+                Block block = new Block(filePosition, bytesCount);
+                Interval interval = new Interval(intervalStart, intervalEnd, block);
+                featureIndex.insert(lastChr, interval);
+
+                // Start new interval
+                featureCount = 1;
+                intervalStart = f.getStart();
+                intervalEnd = f.getEnd();
+                filePosition = currentFilePosition;
+                lastStart = f.getStart();
+                lastChr = chr;
+
+            } else {
+                // Get alignment start and verify file is sorted.
+                int start = f.getStart();
+
+                if (start < lastStart) {
+                    throw new UnsortedFileException(" File must be sorted by start position. " +
+                            "Sort test failed at: " + nextLine);
+                }
+                lastStart = start;
+                intervalEnd = Math.max(f.getEnd(),intervalEnd);
+            }
+
+
+            currentFilePosition = reader.getPosition();
+
+            if (currentFilePosition > (progressCounter * progressIncrement)) {
+                updateProgress(progressCounter);
+                progressCounter++;
+
+            }
+        }
+
+        // Record remainder bin
+        int bytesCount = (int) (currentFilePosition - filePosition);
+        Block block = new Block(filePosition, bytesCount);
+        Interval interval = new Interval(intervalStart, intervalEnd, block);
+        featureIndex.insert(lastChr, interval);
+
+
+        is.close();
+
+        if (idxFile != null) {
+            LittleEndianOutputStream stream = null;
+            try {
+                stream = new LittleEndianOutputStream(new BufferedOutputStream(new FileOutputStream(idxFile)));
+                featureIndex.write(stream);
+            }
+            finally {
+                if (stream != null) {
+                    stream.close();
+                }
+            }
+        }
+
+        return featureIndex;
+    }
+
+
+    private void updateProgress(int progressCounter) {
+        if(verbose) System.out.println("Progress: " + progressCounter + "%");
+    }
+
+    public void setVerbose(boolean verbose) {
+        this.verbose = verbose;
+    }
+}
\ No newline at end of file
diff --git a/src/org/broad/tribble/index/interval/IntervalTree.java b/src/org/broad/tribble/index/interval/IntervalTree.java
new file mode 100644
index 0000000..5cd311c
--- /dev/null
+++ b/src/org/broad/tribble/index/interval/IntervalTree.java
@@ -0,0 +1,504 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble.index.interval;
+
+
+/** An implementation of an interval tree, following the explanation.
+ * from CLR.
+ */
+
+
+import org.apache.log4j.Logger;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class IntervalTree {
+
+    private static Logger logger = Logger.getLogger(IntervalTree.class);
+
+    Node root;
+    Node NIL = Node.NIL;
+    int size;
+
+    public IntervalTree() {
+        this.root = NIL;
+        this.size = 0;
+    }
+
+
+    public void insert(Interval interval) {
+        Node node = new Node(interval);
+        insert(node);
+        size++;
+    }
+
+    public int getSize() {
+        return size;
+    }
+
+
+    // Returns all matches as a list of Intervals
+
+    public List<Interval> findOverlapping(Interval interval) {
+        logger.debug("Starting search for " + interval);
+
+        if (root().isNull()) {
+            return Collections.emptyList();
+        }
+
+        List<Interval> results = new ArrayList();
+        searchAll(interval, root(), results);
+        return results;
+    }
+
+    public String toString() {
+        return root().toString();
+    }
+
+    private List<Interval> searchAll(Interval interval, Node node, List<Interval> results) {
+
+        System.out.println("Visiting " + node.interval);
+
+        if (node.interval.overlaps(interval)) {
+            results.add(node.interval);
+        }
+        if (!node.left.isNull() && node.left.max >= interval.start) {
+            searchAll(interval, node.left, results);
+        }
+        if (!node.right.isNull() && node.right.min <= interval.end) {
+            searchAll(interval, node.right, results);
+        }
+        return results;
+    }
+
+    /**
+     * Return all intervals in tree.
+     * TODO: an iterator would be more effecient.
+     * @return
+     */
+    public List<Interval> getIntervals() {
+        if (root().isNull()) {
+            return Collections.emptyList();
+        }
+        List<Interval> results = new ArrayList(size);
+        getAll(root(), results);
+        return results;
+    }
+
+    private List<Interval> getAll(Node node, List<Interval> results) {
+
+        results.add(node.interval);
+        if (!node.left.isNull()) {
+            getAll(node.left, results);
+        }
+        if (!node.right.isNull()) {
+            getAll(node.right, results);
+        }
+        return results;
+    }
+
+
+    /**
+     * Used for testing only.
+     *
+     * @param node
+     * @return
+     */
+    private int getRealMax(Node node) {
+        if (node.isNull())
+            return Integer.MIN_VALUE;
+        int leftMax = getRealMax(node.left);
+        int rightMax = getRealMax(node.right);
+        int nodeHigh = (node.interval).end;
+
+        int max1 = (leftMax > rightMax ? leftMax : rightMax);
+        return (max1 > nodeHigh ? max1 : nodeHigh);
+    }
+
+    /**
+     * Used for testing only
+     *
+     * @param node
+     * @return
+     */
+    private int getRealMin(Node node) {
+        if (node.isNull())
+            return Integer.MAX_VALUE;
+
+        int leftMin = getRealMin(node.left);
+        int rightMin = getRealMin(node.right);
+        int nodeLow = (node.interval).start;
+
+        int min1 = (leftMin < rightMin ? leftMin : rightMin);
+        return (min1 < nodeLow ? min1 : nodeLow);
+    }
+
+
+    private void insert(Node x) {
+        assert (x != null);
+        assert (!x.isNull());
+
+        treeInsert(x);
+        x.color = Node.RED;
+        while (x != this.root && x.parent.color == Node.RED) {
+            if (x.parent == x.parent.parent.left) {
+                Node y = x.parent.parent.right;
+                if (y.color == Node.RED) {
+                    x.parent.color = Node.BLACK;
+                    y.color = Node.BLACK;
+                    x.parent.parent.color = Node.RED;
+                    x = x.parent.parent;
+                } else {
+                    if (x == x.parent.right) {
+                        x = x.parent;
+                        this.leftRotate(x);
+                    }
+                    x.parent.color = Node.BLACK;
+                    x.parent.parent.color = Node.RED;
+                    this.rightRotate(x.parent.parent);
+                }
+            } else {
+                Node y = x.parent.parent.left;
+                if (y.color == Node.RED) {
+                    x.parent.color = Node.BLACK;
+                    y.color = Node.BLACK;
+                    x.parent.parent.color = Node.RED;
+                    x = x.parent.parent;
+                } else {
+                    if (x == x.parent.left) {
+                        x = x.parent;
+                        this.rightRotate(x);
+                    }
+                    x.parent.color = Node.BLACK;
+                    x.parent.parent.color = Node.RED;
+                    this.leftRotate(x.parent.parent);
+                }
+            }
+        }
+        this.root.color = Node.BLACK;
+    }
+
+
+    private Node root() {
+        return this.root;
+    }
+
+
+    private void leftRotate(Node x) {
+        Node y = x.right;
+        x.right = y.left;
+        if (y.left != NIL) {
+            y.left.parent = x;
+        }
+        y.parent = x.parent;
+        if (x.parent == NIL) {
+            this.root = y;
+        } else {
+            if (x.parent.left == x) {
+                x.parent.left = y;
+            } else {
+                x.parent.right = y;
+            }
+        }
+        y.left = x;
+        x.parent = y;
+
+        applyUpdate(x);
+        // no need to apply update on y, since it'll y is an ancestor
+        // of x, and will be touched by applyUpdate().
+    }
+
+
+    private void rightRotate(Node x) {
+        Node y = x.left;
+        x.left = y.right;
+        if (y.right != NIL) {
+            y.right.parent = x;
+        }
+        y.parent = x.parent;
+        if (x.parent == NIL) {
+            this.root = y;
+        } else {
+            if (x.parent.right == x) {
+                x.parent.right = y;
+            } else {
+                x.parent.left = y;
+            }
+        }
+        y.right = x;
+        x.parent = y;
+
+
+        applyUpdate(x);
+        // no need to apply update on y, since it'll y is an ancestor
+        // of x, and will be touched by applyUpdate().
+    }
+
+
+    /**
+     * Note:  Does not maintain RB constraints,  this is done post insert
+     *
+     * @param x
+     */
+    private void treeInsert(Node x) {
+        Node node = this.root;
+        Node y = NIL;
+        while (node != NIL) {
+            y = node;
+            if (x.interval.start <= node.interval.start) {
+                node = node.left;
+            } else {
+                node = node.right;
+            }
+        }
+        x.parent = y;
+
+        if (y == NIL) {
+            this.root = x;
+            x.left = x.right = NIL;
+        } else {
+            if (x.interval.start <= y.interval.start) {
+                y.left = x;
+            } else {
+                y.right = x;
+            }
+        }
+
+        this.applyUpdate(x);
+    }
+
+
+    // Applies the statistic update on the node and its ancestors.
+
+    private void applyUpdate(Node node) {
+        while (!node.isNull()) {
+            this.update(node);
+            node = node.parent;
+        }
+    }
+
+    private void update(Node node) {
+        node.max = Math.max(Math.max(node.left.max, node.right.max), node.interval.end);
+        node.min = Math.min(Math.min(node.left.min, node.right.min), node.interval.start);
+    }
+
+
+    /**
+     * Returns the number of nodes in the tree.
+     */
+    public int size() {
+        return _size(this.root);
+    }
+
+
+    private int _size(Node node) {
+        if (node.isNull())
+            return 0;
+        return 1 + _size(node.left) + _size(node.right);
+    }
+
+
+    private boolean allRedNodesFollowConstraints(Node node) {
+        if (node.isNull())
+            return true;
+
+        if (node.color == Node.BLACK) {
+            return (allRedNodesFollowConstraints(node.left) &&
+                    allRedNodesFollowConstraints(node.right));
+        }
+
+        // At this point, we know we're on a RED node.
+        return (node.left.color == Node.BLACK &&
+                node.right.color == Node.BLACK &&
+                allRedNodesFollowConstraints(node.left) &&
+                allRedNodesFollowConstraints(node.right));
+    }
+
+
+    // Check that both ends are equally balanced in terms of black height.
+
+    private boolean isBalancedBlackHeight(Node node) {
+        if (node.isNull())
+            return true;
+        return (blackHeight(node.left) == blackHeight(node.right) &&
+                isBalancedBlackHeight(node.left) &&
+                isBalancedBlackHeight(node.right));
+    }
+
+
+    // The black height of a node should be left/right equal.
+
+    private int blackHeight(Node node) {
+        if (node.isNull())
+            return 0;
+        int leftBlackHeight = blackHeight(node.left);
+        if (node.color == Node.BLACK) {
+            return leftBlackHeight + 1;
+        } else {
+            return leftBlackHeight;
+        }
+    }
+
+
+    /**
+     * Test code: make sure that the tree has all the properties
+     * defined by Red Black trees and interval trees
+     * <p/>
+     * o.  Root is black.
+     * <p/>
+     * o.  NIL is black.
+     * <p/>
+     * o.  Red nodes have black children.
+     * <p/>
+     * o.  Every path from root to leaves contains the same number of
+     * black nodes.
+     * <p/>
+     * o.  getMax(node) is the maximum of any interval rooted at that node..
+     * <p/>
+     * This code is expensive, and only meant to be used for
+     * assertions and testing.
+     */
+
+    public boolean isValid() {
+        if (this.root.color != Node.BLACK) {
+            logger.warn("root color is wrong");
+            return false;
+        }
+        if (NIL.color != Node.BLACK) {
+            logger.warn("NIL color is wrong");
+            return false;
+        }
+        if (allRedNodesFollowConstraints(this.root) == false) {
+            logger.warn("red node doesn't follow constraints");
+            return false;
+        }
+        if (isBalancedBlackHeight(this.root) == false) {
+            logger.warn("black height unbalanced");
+            return false;
+        }
+
+        return hasCorrectMaxFields(this.root) &&
+                hasCorrectMinFields(this.root);
+    }
+
+
+    private boolean hasCorrectMaxFields(Node node) {
+        if (node.isNull())
+            return true;
+        return (getRealMax(node) == (node.max) &&
+                hasCorrectMaxFields(node.left) &&
+                hasCorrectMaxFields(node.right));
+    }
+
+
+    private boolean hasCorrectMinFields(Node node) {
+        if (node.isNull())
+            return true;
+        return (getRealMin(node) == (node.min) &&
+                hasCorrectMinFields(node.left) &&
+                hasCorrectMinFields(node.right));
+    }
+
+
+    static class Node {
+
+        public static boolean BLACK = false;
+        public static boolean RED = true;
+
+        Interval interval;
+        int min;
+        int max;
+        Node left;
+        Node right;
+
+        // Color and parent are used for inserts.  If tree is immutable these are not required (no requirement
+        // to store these persistently).
+        boolean color;
+        Node parent;
+
+
+        private Node() {
+            this.max = Integer.MIN_VALUE;
+            this.min = Integer.MAX_VALUE;
+        }
+
+        public void store(DataOutputStream dos) throws IOException {
+            dos.writeInt(interval.start);
+            dos.writeInt(interval.end);
+            dos.writeInt(min);
+            dos.writeInt(max);
+
+        }
+
+        public Node(Interval interval) {
+            this();
+            this.parent = NIL;
+            this.left = NIL;
+            this.right = NIL;
+            this.interval = interval;
+            this.color = RED;
+        }
+
+
+        static Node NIL;
+
+        static {
+            NIL = new Node();
+            NIL.color = BLACK;
+            NIL.parent = NIL;
+            NIL.left = NIL;
+            NIL.right = NIL;
+        }
+
+
+        public boolean isNull() {
+            return this == NIL;
+        }
+
+
+        public String toString() {
+            if (this == NIL) {
+                return "nil";
+            }
+            /* return
+                    "(" + this.interval + " " + (this.color == RED ? "RED" : "BLACK") +
+                            " (" + this.left.toString() + ", " + this.right.toString() + ")";
+                            */
+            StringBuffer buf = new StringBuffer();
+            _toString(buf);
+            return buf.toString();
+        }
+
+        public void _toString(StringBuffer buf) {
+            if (this == NIL) {
+                buf.append("nil");
+                return;
+            }
+            buf.append(this.interval + " -> " + this.left.interval + ", " + this.right.interval);
+            buf.append("\n");
+            this.left._toString(buf);
+            this.right._toString(buf);
+        }
+    }
+}
+
diff --git a/src/org/broad/tribble/index/interval/IntervalTreeIndex.java b/src/org/broad/tribble/index/interval/IntervalTreeIndex.java
new file mode 100644
index 0000000..8287d21
--- /dev/null
+++ b/src/org/broad/tribble/index/interval/IntervalTreeIndex.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble.index.interval;
+
+import org.broad.tribble.index.AbstractIndex;
+import org.broad.tribble.index.Block;
+import org.broad.tribble.index.IndexFactory;
+import org.broad.tribble.util.LittleEndianInputStream;
+import org.broad.tribble.util.LittleEndianOutputStream;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * @author jrobinso
+ * Date: Jul 9, 2010
+ */
+public class IntervalTreeIndex extends AbstractIndex {
+
+
+    /**
+     * Default constructor -- used by factory methods.  Do not remove.
+     */
+    public IntervalTreeIndex() {
+
+    }
+
+    public IntervalTreeIndex(String featureFile) {
+        super(featureFile);
+    }
+
+    @Override
+    public Class getIndexClass() {
+        return ChrIndex.class;
+    }
+
+        @Override
+    protected int getType() {
+        return IndexFactory.INTERVAL_TREE;
+    }
+
+    public void insert(String chr, Interval interval) {
+        ChrIndex chrIdx = (ChrIndex) chrIndeces.get(chr);
+        if (chrIdx == null) {
+            chrIdx = new ChrIndex(chr);
+            chrIndeces.put(chr, chrIdx);
+        }
+        chrIdx.insert(interval);
+    }
+
+
+    public void printTree() {
+
+        for (String chr : chrIndeces.keySet()) {
+            System.out.println(chr + ":");
+            ChrIndex chrIdx = (ChrIndex) chrIndeces.get(chr);
+            chrIdx.printTree();
+            System.out.println();
+        }
+    }
+
+
+
+    public static class ChrIndex implements org.broad.tribble.index.ChrIndex {
+
+        IntervalTree tree;
+        String name;
+
+        /**
+         * Default constructor needed for factory methods -- DO NOT REMOVE
+         */
+        public ChrIndex() {
+
+        }
+
+        public ChrIndex(String name) {
+            this.name = name;
+            tree = new IntervalTree();
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void insert(Interval iv) {
+            tree.insert(iv);
+        }
+
+        public List<Block> getBlocks(int start, int end) {
+
+            // Get intervals and build blocks list
+            List<Interval> intervals = tree.findOverlapping(new Interval(start, end));
+
+            // save time (and save throwing an exception) if the blocks are empty, return now
+            if (intervals == null || intervals.size() == 0) return new ArrayList<Block>();
+
+            Block[] blocks = new Block[intervals.size()];
+            int idx = 0;
+            for (Interval iv : intervals) {
+                blocks[idx++] = iv.getBlock();
+            }
+
+            // Sort blocks by start position
+            Arrays.sort(blocks, new Comparator<Block>() {
+                public int compare(Block b1, Block b2) {
+                    return (int) (b1.getStartPosition() - b2.getStartPosition());
+                }
+            });
+
+            // Consolidate blocks  that are close together
+            List<Block> consolidatedBlocks = new ArrayList(blocks.length);
+            Block lastBlock = blocks[0];
+            consolidatedBlocks.add(lastBlock);
+            for (int i = 1; i < blocks.length; i++) {
+                Block block = blocks[i];
+                if (block.getStartPosition() < (lastBlock.getEndPosition() + 1000)) {
+                    lastBlock.setEndPosition(block.getEndPosition());
+                } else {
+                    lastBlock = block;
+                    consolidatedBlocks.add(lastBlock);
+                }
+            }
+
+            return consolidatedBlocks;
+        }
+
+        public void printTree() {
+            System.out.println(tree.toString());
+        }
+
+        public void write(LittleEndianOutputStream dos) throws IOException {
+
+            dos.writeString(name);
+            List<Interval> intervals = tree.getIntervals();
+
+            dos.writeInt(intervals.size());
+            for (Interval interval : intervals) {
+                dos.writeInt(interval.start);
+                dos.writeInt(interval.end);
+                dos.writeLong(interval.getBlock().getStartPosition());
+                dos.writeInt(interval.getBlock().getSize());
+            }
+
+        }
+
+        public void read(LittleEndianInputStream dis) throws IOException {
+
+            tree = new IntervalTree();
+
+            name = dis.readString();
+            int nIntervals = dis.readInt();
+            while (nIntervals-- > 0) {
+
+                int start = dis.readInt();
+                int end = dis.readInt();
+                long pos = dis.readLong();
+                int size = dis.readInt();
+
+                Interval iv = new Interval(start, end, new Block(pos, size));
+                tree.insert(iv);
+            }
+
+
+        }
+
+    }
+}
diff --git a/src/org/broad/tribble/index/linear/LinearIndex.java b/src/org/broad/tribble/index/linear/LinearIndex.java
new file mode 100644
index 0000000..06ee621
--- /dev/null
+++ b/src/org/broad/tribble/index/linear/LinearIndex.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2009-2010 by The Broad Institute, Inc.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble.index.linear;
+
+import org.apache.log4j.Logger;
+import org.broad.tribble.index.AbstractIndex;
+import org.broad.tribble.index.Block;
+import org.broad.tribble.index.Index;
+import org.broad.tribble.index.IndexFactory;
+import org.broad.tribble.util.LittleEndianInputStream;
+import org.broad.tribble.util.LittleEndianOutputStream;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Index defined by dividing the genome by chromosome, then each chromosome into bins of fixed width (in
+ * genomic coordinates).   Features are allocated to bins by start position.  The longest feature in each
+ * recorded and used to adjust the start position of a query to include all bins that might have a feature
+ * that overlaps the query interval.  This works well for feature sets of approximately homogeneous length,
+ * or whose longest feature is on the order of the bin width or less.
+ * <p/>
+ * magicNumber      integer
+ * type             integer
+ * version          integer
+ * filename         null terminated character array
+ * filesize         long
+ * lastModified     long
+ * md5              String
+ * flags            integer
+ * <p/>
+ * ------  LINEAR INDEX
+ * nChromosomes     integer
+ */
+
+public class LinearIndex extends AbstractIndex implements Index {
+
+    private static Logger log = Logger.getLogger(LinearIndex.class);
+
+    /**
+     * Width of each block in base pairs
+     */
+    private int defaultBinWidth;
+    /**
+     * Default constructor -- used by factory methods.  Do not remove.
+     */
+    public LinearIndex() {
+
+    }
+
+    public LinearIndex(int binWidth, String featureFile) {
+        super(featureFile);
+        this.defaultBinWidth = binWidth;
+    }
+
+    @Override
+    protected int getType() {
+        return IndexFactory.LINEAR;
+    }
+
+    /**
+     * Record a bin position and size.
+     */
+    public void add(String chr, long idx, int size, int longestFeature) {
+
+        ChrIndex chrIndex = (ChrIndex) chrIndeces.get(chr);
+        if (chrIndex == null) {
+            chrIndex = new ChrIndex(chr, defaultBinWidth);
+            chrIndeces.put(chr, chrIndex);
+        }
+        chrIndex.updateLongestFeature(longestFeature);
+        chrIndex.addBlock(new Block(idx, size));
+
+
+    }
+
+
+    public LinkedHashSet<String> getSequenceNames() {
+        return (chrIndeces == null ? new LinkedHashSet() : new LinkedHashSet(chrIndeces.keySet()));
+    }
+
+    @Override
+    public Class getIndexClass() {
+        return ChrIndex.class;
+    }
+
+
+    public static class ChrIndex implements org.broad.tribble.index.ChrIndex {
+        private String name;
+        private int binWidth;
+        private int longestFeature;
+        private int largestBlockSize;
+        private int totalBlockSize;
+        private List<Block> blocks;
+
+        /**
+         * Default constructor needed for factory methods -- DO NOT REMOVE
+         */
+        public ChrIndex() {
+
+        }
+
+
+        ChrIndex(LittleEndianInputStream dis) throws IOException {
+            read(dis);
+        }
+
+        ChrIndex(String name, int binWidth) {
+            this.name = name;
+            this.binWidth = binWidth;
+            this.blocks = new ArrayList(100);
+            this.longestFeature = 0;
+            this.largestBlockSize = 0;
+            this.totalBlockSize = 0;
+        }
+
+
+        public String getName() {
+            return name;
+        }
+
+        void addBlock(Block block) {
+            blocks.add(block);
+            totalBlockSize += block.getSize();
+            largestBlockSize = Math.max(largestBlockSize, block.getSize());
+        }
+
+        public List<Block> getBlocks(int start, int end) {
+            if (blocks == null || blocks.isEmpty()) {
+                // TODO -- throw exception ?
+                return null;
+            }
+            // Adjust position for the longest feature in this chromosome.  This insures we get features that start
+            // before the bin but extend into it
+            int adjustedPosition = Math.max(start - longestFeature, 0);
+
+            int startBinNumber = Math.min(adjustedPosition / binWidth, blocks.size() - 1);
+            // are we off the end of the bin list
+
+            int endBinNumber = Math.min(end / binWidth, blocks.size() - 1);
+
+            return blocks.subList(startBinNumber, endBinNumber + 1);
+        }
+
+
+        public void updateLongestFeature(int featureLength) {
+            longestFeature = Math.max(longestFeature, featureLength);
+        }
+
+        public void write(LittleEndianOutputStream dos) throws IOException {
+
+            // Chr name, binSize,  # bins,  longest feature
+            dos.writeString(name);
+            dos.writeInt(binWidth);
+            dos.writeInt(blocks.size());
+            dos.writeInt(longestFeature);
+            dos.writeInt(largestBlockSize);
+            dos.writeInt(totalBlockSize);
+
+            long pos = 0;
+            int size = 0;
+            for (Block block : blocks) {
+                pos = block.getStartPosition();
+                size = block.getSize();
+                dos.writeLong(pos);
+            }
+            // End of last block for this chromosome
+            dos.writeLong(pos + size);
+        }
+
+        public void read(LittleEndianInputStream dis) throws IOException {
+            name = dis.readString();
+            binWidth = dis.readInt();
+            int nBins = dis.readInt();
+            longestFeature = dis.readInt();
+            largestBlockSize = dis.readInt();
+            totalBlockSize = dis.readInt();
+
+            blocks = new ArrayList(nBins);
+            long pos = dis.readLong();
+            for (int binNumber = 0; binNumber < nBins; binNumber++) {
+                long nextPos = dis.readLong();
+                int size = (int) (nextPos - pos);
+                blocks.add(new Block(pos, size));
+                pos = nextPos;
+            }
+
+        }
+
+    }
+}
+
diff --git a/src/org/broad/tribble/index/linear/LinearIndexCreator.java b/src/org/broad/tribble/index/linear/LinearIndexCreator.java
new file mode 100644
index 0000000..248a235
--- /dev/null
+++ b/src/org/broad/tribble/index/linear/LinearIndexCreator.java
@@ -0,0 +1,212 @@
+package org.broad.tribble.index.linear;
+
+import org.apache.log4j.Logger;
+import org.broad.tribble.Feature;
+import org.broad.tribble.FeatureCodec;
+import org.broad.tribble.exception.UnsortedFileException;
+import org.broad.tribble.index.Index;
+import org.broad.tribble.index.IndexCreator;
+import org.broad.tribble.util.AsciiLineReader;
+import org.broad.tribble.util.LittleEndianOutputStream;
+import org.broad.tribble.util.ParsingUtils;
+
+import java.io.*;
+
+/**
+ * @author jrobinso
+ */
+public class LinearIndexCreator extends IndexCreator {
+
+    private static Logger log = Logger.getLogger(LinearIndexCreator.class);
+
+    static int DEFAULT_BINWIDTH = 16000;
+
+    private int binWidth;
+
+    private boolean verbose = true;
+
+    public LinearIndexCreator(File featureFile, FeatureCodec codec) {
+        super(featureFile, codec);
+        this.binWidth = DEFAULT_BINWIDTH;
+    }
+
+
+    public Index createIndex() throws IOException {
+
+        LinearIndex featureIndex = new LinearIndex(binWidth, featureFile.getAbsolutePath());
+
+        FileInputStream is = new FileInputStream(featureFile);
+        AsciiLineReader reader = new AsciiLineReader(is);
+
+        // make sure to read the header off first
+        codec.readHeader(reader);
+
+        long fileLength = featureFile.length();
+        long progressIncrement = fileLength / 100;
+        long currentFilePosition = 0;
+        String lastChr = null;
+        int lastStart = 0;
+        int lastBinNumber = 0;
+        int lastBinStart = -1;
+        int longestFeature = 0;
+        int progressCounter = 1; // progress in %
+        int lineNumber = 0;
+
+        String nextLine = "";
+
+        // Skip to first feature (past header
+        long filePosition;
+        Feature f = null;
+        do {
+            lineNumber++;
+            filePosition = reader.getPosition();
+            nextLine = reader.readLine();
+        } while (nextLine != null && (f = codec.decodeLoc(nextLine)) == null);
+
+        // First non-null feature.  "f" should never be null here.
+        if (f != null) {
+            lastChr = f.getChr();
+            lastBinStart = f.getStart();
+        }
+        currentFilePosition = reader.getPosition();
+
+        while ((nextLine = reader.readLine()) != null) {
+            lineNumber++;
+
+            f = codec.decodeLoc(nextLine);
+            if (f == null) {
+                continue;
+            }
+
+            String chr = f.getChr();
+
+            if (!chr.equals(lastChr)) {
+                // New chromosome.  Start a new bin
+
+                int bytesCount = (int) (currentFilePosition - filePosition);
+                featureIndex.add(lastChr, filePosition, bytesCount, longestFeature);
+                lastBinStart = f.getStart();
+                filePosition = currentFilePosition;
+                lastBinNumber = 0;
+                lastStart = 0;
+                longestFeature = 0;
+
+                lastChr = chr;
+
+            } else {
+                // Get alignment start and verify file is sorted.
+                int start = f.getStart();
+
+                longestFeature = Math.max(longestFeature, f.getEnd() - start);
+
+                if (start < lastStart) {
+                    throw new UnsortedFileException(" File must be sorted by start position. " +
+                            "Sort test failed at: " + nextLine);
+                }
+
+                lastStart = start;
+
+                int binNumber = start / binWidth;
+                if (binNumber > lastBinNumber) {
+
+                    int cnt = 0;
+                    // if the first record isn't in the first tile, we have to fill in blank tiles
+                    for (cnt = 0; cnt < ((lastBinStart / binWidth) - lastBinNumber); cnt++) {
+                        featureIndex.add(lastChr, filePosition, 0, longestFeature);
+
+                    }
+                    lastBinNumber += cnt;
+                    // We have crossed a tile boundary.  Record index and counts for previous tile
+                    int bytesCount = (int) (currentFilePosition - filePosition);
+                    featureIndex.add(lastChr, filePosition, bytesCount, longestFeature);
+                    lastBinStart = f.getStart();
+                    filePosition = currentFilePosition;
+
+                    // If tiles were skipped record zero counts for these.
+                    for (cnt = 0; cnt < (binNumber - lastBinNumber - 1); cnt++) {
+                        featureIndex.add(lastChr, filePosition, 0, longestFeature);
+                    }
+
+
+                    lastBinNumber = binNumber;
+                }
+            }
+
+
+            currentFilePosition = reader.getPosition();
+
+            if (currentFilePosition > (progressCounter * progressIncrement)) {
+                updateProgress(progressCounter);
+                progressCounter++;
+
+            }
+        }
+
+        // Record remainder bin
+        int byteCount = (int) (reader.getPosition() - filePosition);
+        featureIndex.add(lastChr, filePosition, byteCount, longestFeature);
+
+
+        is.close();
+
+        if (idxFile != null) {
+            LittleEndianOutputStream stream = null;
+            try {
+                stream = new LittleEndianOutputStream(new BufferedOutputStream(new FileOutputStream(idxFile)));
+                featureIndex.write(stream);
+            }
+            finally {
+                if (stream != null) {
+                    stream.close();
+                }
+            }
+        }
+
+        return featureIndex;
+    }
+
+    private void updateProgress(int progressCounter) {
+        if(verbose) System.out.println("Progress: " + progressCounter + "%");
+    }
+
+    /*
+    public String checksum(File file) {
+  try {
+    InputStream fin = new FileInputStream(file);
+    java.security.MessageDigest md5er =
+        MessageDigest.getInstance("MD5");
+    byte[] buffer = new byte[1024];
+    int read;
+    do {
+      read = fin.read(buffer);
+      if (read > 0)
+        md5er.update(buffer, 0, read);
+    } while (read != -1);
+    fin.close();
+    byte[] digest = md5er.digest();
+    if (digest == null)
+      return null;
+    String strDigest = "0x";
+    for (int i = 0; i < digest.length; i++) {
+      strDigest += Integer.toString((digest[i] & 0xff)
+                + 0x100, 16).substring(1).toUpperCase();
+    }
+    return strDigest;
+  } catch (Exception e) {
+    return null;
+  }
+}
+     */
+
+    public int getBinWidth() {
+        return binWidth;
+    }
+
+    public void setBinWidth(int binWidth) {
+        this.binWidth = binWidth;
+    }
+
+    public void setVerbose(boolean verbose) {
+        this.verbose = verbose;
+    }
+}
diff --git a/src/org/broad/tribble/index/linear/LinearIndexerTest.java b/src/org/broad/tribble/index/linear/LinearIndexerTest.java
new file mode 100644
index 0000000..ec8ed60
--- /dev/null
+++ b/src/org/broad/tribble/index/linear/LinearIndexerTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble.index.linear;
+
+import org.broad.igv.track.tribble.BEDCodec;
+import org.broad.tribble.index.Block;
+import org.broad.tribble.index.Index;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: Dec 21, 2009
+ * Time: 9:04:39 AM
+ * To change this template use File | Settings | File Templates.
+ */
+public class LinearIndexerTest {
+
+
+    @Test
+    public void testIndexBED() throws IOException {
+
+        File testFile = new File("test/data/test.bed");
+
+        LinearIndexCreator indexer = new LinearIndexCreator(testFile, new BEDCodec());
+        Index idx = indexer.createIndex();
+
+        Block block = idx.getBlocks("chr1", 100, 200).get(0);
+        assertEquals(33, block.getStartPosition());
+        assertEquals(54, block.getSize());
+
+        block = idx.getBlocks("chr1", 20000, 20001).get(0);
+        assertEquals(87, block.getStartPosition());
+        assertEquals(0, block.getSize());
+
+    }
+
+ }
diff --git a/src/org/broad/tribble/readers/AsciiQueryReader.java b/src/org/broad/tribble/readers/AsciiQueryReader.java
new file mode 100644
index 0000000..7de20d8
--- /dev/null
+++ b/src/org/broad/tribble/readers/AsciiQueryReader.java
@@ -0,0 +1,133 @@
+package org.broad.tribble.readers;
+
+import org.apache.log4j.Logger;
+import org.broad.tribble.index.Block;
+import org.broad.tribble.index.Index;
+import org.broad.tribble.index.IndexFactory;
+import org.broad.tribble.util.*;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: May 17, 2010
+ * Time: 3:26:34 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class AsciiQueryReader implements QueryReader {
+
+    private static Logger log = Logger.getLogger(BasicFeatureReader.class);
+
+    protected SeekableStream seekableStream;
+    protected Index index;
+
+    public AsciiQueryReader(String featureFile, String indexFile) throws IOException {
+        seekableStream = SeekableStreamFactory.getStreamFor(featureFile);
+        index = IndexFactory.loadIndex(indexFile);
+    }
+
+    public AsciiQueryReader(String featureFile) throws IOException {
+        String indexFile = featureFile + ".idx";
+        seekableStream = SeekableStreamFactory.getStreamFor(featureFile);
+        index = IndexFactory.loadIndex(indexFile);
+    }
+
+    public AsciiQueryReader(String featureFile, Index indexInstance) throws IOException {
+        seekableStream = SeekableStreamFactory.getStreamFor(featureFile);
+        this.index = indexInstance;
+    }
+
+
+    public void close() throws IOException {
+        if (seekableStream != null) {
+            seekableStream.close();
+        }
+    }
+
+    public LineReader iterate() throws IOException {
+        seekableStream.seek(0);
+        return new AsciiLineReader(seekableStream);
+    }
+
+    public LineReader query(String chr, int start, int end) {
+        return new IndexedReader(chr, start, end);
+    }
+
+    public Set<String> getSequenceNames() {
+        return index.getSequenceNames();
+    }
+
+
+
+    public class IndexedReader implements LineReader {
+
+        String chr;
+        int start;
+        int end;
+
+        AsciiLineReader reader;
+        List<Block> blocks;
+
+
+        // Return a reader to loop over the whole file
+
+        public IndexedReader() {
+            reader = new AsciiLineReader(seekableStream);
+        }
+
+        // Return a reader to loop over to stream over a query inerval
+
+        public IndexedReader(String chr, int start, int end) {
+            this.chr = chr;
+            this.start = start;
+            this.end = end;
+            init();
+        }
+
+        public String readLine() throws IOException {
+            return reader == null ? null : reader.readLine();
+        }
+
+        public void close() {
+            if(reader != null) reader.close();
+        }
+
+
+        /**
+         * Initialize the reader
+         * <p/>
+         * todo -- the block collection is really not used, this implementation will stream over entire extent
+         * todo -- covered by range of blocks.
+         */
+        private void init() {
+
+            if (index == null) {
+                throw new UnsupportedOperationException("Files must be indexed to support query methods");
+            }
+
+            blocks = index.getBlocks(chr, start, end);
+
+            if (blocks == null || blocks.size() == 0) {
+                // No features for this query
+                reader = null;
+
+            } else {
+                Block firstBlock = blocks.get(0);
+                Block lastBlock = blocks.get(blocks.size() - 1);
+
+                try {
+                    seekableStream.seek(firstBlock.getStartPosition());
+                    reader = new AsciiLineReader(seekableStream);
+                } catch (IOException ex) {
+                    log.error("Error seeking to position: " + firstBlock.getStartPosition(), ex);
+                    // TODO -- throw application exception?
+                }
+            }
+
+        }
+    }
+
+}
diff --git a/src/org/broad/tribble/readers/BasicFeatureReader.java b/src/org/broad/tribble/readers/BasicFeatureReader.java
new file mode 100644
index 0000000..4ad1de1
--- /dev/null
+++ b/src/org/broad/tribble/readers/BasicFeatureReader.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble.readers;
+
+import org.apache.log4j.Logger;
+import org.broad.tribble.Feature;
+import org.broad.tribble.FeatureCodec;
+import org.broad.tribble.FeatureReader;
+import org.broad.tribble.index.Index;
+import org.broad.tribble.util.AsciiLineReader;
+import org.broad.tribble.util.CloseableTribbleIterator;
+import org.broad.tribble.util.LineReader;
+import org.broad.tribble.util.ParsingUtils;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * jrobinso
+ * <p/>
+ * the feature reader class, which uses indices and codecs to read in Tribble file formats.
+ */
+public class BasicFeatureReader<T extends Feature> implements FeatureReader {
+
+    private static Logger log = Logger.getLogger(BasicFeatureReader.class);
+
+
+    String path;
+
+    //
+    private final QueryReader querySource;
+    private final FeatureCodec codec;
+    private final Object header;
+
+    /**
+     * Constructor for unknown file type,  could be ascii or could be tabix, or something else.
+     *
+     * @param featureFile
+     * @param codec
+     * @throws FileNotFoundException
+     */
+
+    public BasicFeatureReader(String featureFile, FeatureCodec codec) throws IOException {
+
+        this.path = featureFile;
+
+        // Crude test for now
+        if (featureFile.endsWith(".gz") && TabixReader.isTabix(featureFile)) {
+            querySource = new TabixReader(featureFile);
+        } else {
+            String indexFile = featureFile + ".idx";
+            querySource = new AsciiQueryReader(featureFile, indexFile);
+        }
+        this.codec = codec;
+        header = readHeader();
+    }
+
+    /**
+     * Constructor for ascii indexed files
+     *
+     * @param featureFile
+     * @param indexInstance
+     * @param codec
+     * @throws FileNotFoundException
+     */
+    public BasicFeatureReader(String featureFile, Index indexInstance, FeatureCodec codec) throws IOException {
+        this.path = featureFile;
+        this.codec = codec;
+        querySource = new AsciiQueryReader(featureFile, indexInstance);
+        header = readHeader();
+    }
+
+    /**
+     * Constructor for ascii indexed files
+     *
+     * @param featureFile
+     * @param indexFile
+     * @param codec
+     * @throws FileNotFoundException
+     */
+    public BasicFeatureReader(String featureFile, String indexFile, FeatureCodec codec) throws IOException {
+        this.path = featureFile;
+        querySource = new AsciiQueryReader(featureFile, indexFile);
+        this.codec = codec;
+        header = readHeader();
+    }
+
+
+    private Object readHeader() throws IOException {
+        AsciiLineReader reader = null;
+        try {
+            reader = ParsingUtils.openAsciiReader(path);
+            return codec.readHeader(reader);
+        }
+        finally {
+            if (reader != null) {
+                reader.close();
+            }
+        }
+    }
+
+    public Object getHeader() {
+        return header;
+    }
+
+
+    public void close() throws IOException {
+        if (querySource != null) {
+            querySource.close();
+        }
+    }
+
+    public CloseableTribbleIterator<T> query(final String chr, final int start, final int end) throws IOException {
+        return query(chr, start, end, false);
+    }
+
+    public CloseableTribbleIterator<T> iterator() throws IOException {
+        return new IteratorImpl<T>(this);
+    }
+
+    public CloseableTribbleIterator<T> query(final String chr, final int start, final int end, final boolean contained) throws IOException {
+
+        return new IteratorImpl<T>(this, chr, start, end, contained);
+    }
+
+
+    public Set<String> getSequenceNames() {
+        return querySource.getSequenceNames();
+    }
+
+    /**
+     * the basic feature iterator for indexed files
+     */
+    public static class IteratorImpl<T extends Feature> implements CloseableTribbleIterator {
+
+        private static Logger log = Logger.getLogger("IteratorImpl");
+
+        String chr;
+        int start;
+        int end;
+        boolean contained;
+        T currentRecord;
+
+        final LineReader reader;
+
+        BasicFeatureReader<T> BasicFeatureReader;
+
+        IteratorImpl(BasicFeatureReader<T> BasicFeatureReader) throws IOException {
+            this.BasicFeatureReader = BasicFeatureReader;
+            reader = BasicFeatureReader.querySource.iterate();
+            // we have to read the header off in this case (since the reader seeked to position 0)
+            readNextRecord();
+        }
+
+        IteratorImpl(BasicFeatureReader<T> BasicFeatureReader,
+                     String sequence,
+                     int start,
+                     int end,
+                     boolean contained) throws IOException {
+
+            this.BasicFeatureReader = BasicFeatureReader;
+            this.chr = sequence;
+            this.start = start;
+            this.end = end;
+            this.contained = contained;
+            reader = BasicFeatureReader.querySource.query(chr, start, end);
+
+            advanceToFirstRecord();
+        }
+
+        private T readNextRecord() throws IOException {
+            currentRecord = null;
+            String nextLine;
+            while (currentRecord == null && reader != null && (nextLine = reader.readLine()) != null) {
+                Feature f = null;
+                f = BasicFeatureReader.codec.decode(nextLine);
+                if (f == null)
+                    continue;
+                else if ((end > 0 && f.getStart() > end) || (chr != null && !chr.equals(f.getChr())))
+                    break;
+                else if (f.getEnd() >= start)
+                    currentRecord = (T) f;
+            }
+            return currentRecord;
+        }
+
+        private void advanceToFirstRecord() throws IOException {
+            T record = readNextRecord();
+            while (record != null) {
+                if (!currentRecord.getChr().equals(chr)) {
+                    break;
+                } else if ((contained && currentRecord.getStart() >= start) ||
+                        (!contained && currentRecord.getEnd() >= start)) {
+                    break;
+                } else if (currentRecord.getStart() > end) {
+                    currentRecord = null;
+                    break;
+                }
+                record = readNextRecord();
+            }
+        }
+
+        public boolean hasNext() {
+
+            // chr == null => iterator, not query.  Fix this
+            if (chr == null) {
+                return currentRecord != null;
+            }
+
+            return !(currentRecord == null ||
+                    !chr.equals(currentRecord.getChr())) && (contained ? currentRecord.getEnd() <= end : currentRecord.getStart() <= end);
+        }
+
+        public T next() {
+            T ret = currentRecord;
+            try {
+                readNextRecord();
+            } catch (IOException e) {
+                throw new RuntimeException("Unable to read the next record, the last record was at " + ret.getChr() + ":" + ret.getStart() + "-" + ret.getEnd(), e);
+            }
+            return ret;
+
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException("Remove is not supported in Iterators");
+        }
+
+
+        public void close() {
+            // we don't have anything open, so don't sweat it
+        }
+
+        public Iterator<Feature> iterator() {
+            return this;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/org/broad/tribble/readers/QueryReader.java b/src/org/broad/tribble/readers/QueryReader.java
new file mode 100644
index 0000000..4ee99e5
--- /dev/null
+++ b/src/org/broad/tribble/readers/QueryReader.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble.readers;
+
+import org.broad.tribble.util.LineReader;
+
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: May 17, 2010
+ * Time: 10:06:25 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public interface QueryReader {
+
+    LineReader iterate() throws IOException;
+
+    LineReader query(String chr, int start, int end);
+
+    void close()  throws IOException;
+
+    public Set<String> getSequenceNames();
+}
\ No newline at end of file
diff --git a/src/org/broad/tribble/readers/TabixReader.java b/src/org/broad/tribble/readers/TabixReader.java
new file mode 100644
index 0000000..f7f9477
--- /dev/null
+++ b/src/org/broad/tribble/readers/TabixReader.java
@@ -0,0 +1,490 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble.readers;
+
+import net.sf.samtools.util.BlockCompressedInputStream;
+import org.broad.tribble.util.LineReader;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Set;
+
+
+/* Contact: Heng Li <hengli at broadinstitute.org> */
+
+
+public class TabixReader implements QueryReader, LineReader {
+    private String filename;
+    private BlockCompressedInputStream blockCompressedInputStream;
+
+    private int format;
+    private int seqCol;
+    private int begCol;
+    private int endCol;
+    private int metaChar;
+    private int nSkip;
+    private String[] sequences;
+
+    private HashMap<String, Integer> chr2tid;
+
+    private static int MAX_BIN = 37450;
+    private static int TAD_MIN_CHUNK_GAP = 32768;
+    private static int TAD_LIDX_SHIFT = 14;
+
+    private class TPair64 implements Comparable<TPair64> {
+        long u, v;
+
+        public TPair64(final long _u, final long _v) {
+            u = _u;
+            v = _v;
+        }
+
+        public TPair64(final TPair64 p) {
+            u = p.u;
+            v = p.v;
+        }
+
+        public int compareTo(final TPair64 p) {
+            return u == p.u ? 0 : ((u < p.u) ^ (u < 0) ^ (p.u < 0)) ? -1 : 1; // unsigned 64-bit comparison
+        }
+    }
+
+    private class TIndex {
+        HashMap<Integer, TPair64[]> b; // binning index
+        long[] l; // linear index
+    }
+
+
+    private TIndex[] mIndex;
+
+    private class TIntv {
+        int tid, beg, end;
+    }
+
+
+    private static boolean less64(final long u, final long v) { // unsigned 64-bit comparison
+        return (u < v) ^ (u < 0) ^ (v < 0);
+    }
+
+    /**
+     * The constructor
+     *
+     * @param fn File name of the data file
+     */
+    public TabixReader(final String fn) throws IOException {
+        filename = fn;
+        if (fn.startsWith("http:") || fn.startsWith("https:")) {
+            blockCompressedInputStream = new BlockCompressedInputStream(new URL(fn));
+        } else {
+            blockCompressedInputStream = new BlockCompressedInputStream(new File(fn));
+        }
+        readIndex();
+    }
+
+    public Set<String> getSequenceNames() {
+        return chr2tid.keySet();
+    }
+
+    private static int reg2bins(final int beg, final int _end, final int[] bins) {
+        int i = 0, k, end = _end;
+        --end;
+        bins[i++] = 0;
+        for (k = 1 + (beg >> 26); k <= 1 + (end >> 26); ++k) bins[i++] = k;
+        for (k = 9 + (beg >> 23); k <= 9 + (end >> 23); ++k) bins[i++] = k;
+        for (k = 73 + (beg >> 20); k <= 73 + (end >> 20); ++k) bins[i++] = k;
+        for (k = 585 + (beg >> 17); k <= 585 + (end >> 17); ++k) bins[i++] = k;
+        for (k = 4681 + (beg >> 14); (k <= 4681 + (end >> 14)) && i < bins.length; ++k) bins[i++] = k;
+        return i;
+    }
+
+    public static int readInt(final InputStream is) throws IOException {
+        byte[] buf = new byte[4];
+        is.read(buf);
+        return ByteBuffer.wrap(buf).order(ByteOrder.LITTLE_ENDIAN).getInt();
+    }
+
+    public static long readLong(final InputStream is) throws IOException {
+        byte[] buf = new byte[8];
+        is.read(buf);
+        return ByteBuffer.wrap(buf).order(ByteOrder.LITTLE_ENDIAN).getLong();
+    }
+
+    public static String readNextLine(final InputStream is) throws IOException {
+        StringBuffer buf = new StringBuffer();
+        int c;
+        while ((c = is.read()) >= 0 && c != '\n')
+            buf.append((char) c);
+        if (c < 0) return null;
+        return buf.toString();
+    }
+
+    /**
+     * Read the Tabix index from a file
+     */
+    public void readIndex(BlockCompressedInputStream is) throws IOException {
+        byte[] buf = new byte[4];
+
+        is.read(buf, 0, 4); // read "TBI\1"
+        sequences = new String[readInt(is)]; // # sequences
+        chr2tid = new HashMap<String, Integer>();
+        format = readInt(is);   // format
+        seqCol = readInt(is);
+        begCol = readInt(is);
+        endCol = readInt(is);
+        metaChar = readInt(is);
+        nSkip = readInt(is);
+        // read sequence dictionary
+        int i, j, k, l = readInt(is);
+        buf = new byte[l];
+        is.read(buf);
+        for (i = j = k = 0; i < buf.length; ++i) {
+            if (buf[i] == 0) {
+                byte[] b = new byte[i - j];
+                System.arraycopy(buf, j, b, 0, b.length);
+                String s = new String(b);
+                chr2tid.put(s, k);
+                sequences[k++] = s;
+                j = i + 1;
+            }
+        }
+        // read the index
+        mIndex = new TIndex[sequences.length];
+        for (i = 0; i < sequences.length; ++i) {
+            // the binning index
+            int n_bin = readInt(is);
+            mIndex[i] = new TIndex();
+            mIndex[i].b = new HashMap<Integer, TPair64[]>();
+            for (j = 0; j < n_bin; ++j) {
+                int bin = readInt(is);
+                TPair64[] chunks = new TPair64[readInt(is)];
+                for (k = 0; k < chunks.length; ++k) {
+                    long u = readLong(is);
+                    long v = readLong(is);
+                    chunks[k] = new TPair64(u, v); // in C, this is inefficient
+                }
+                mIndex[i].b.put(bin, chunks);
+            }
+            // the linear index
+            mIndex[i].l = new long[readInt(is)];
+            for (k = 0; k < mIndex[i].l.length; ++k)
+                mIndex[i].l[k] = readLong(is);
+        }
+        // close
+        is.close();
+    }
+
+    /**
+     * Read the Tabix index from the default file.
+     */
+    public void readIndex() throws IOException {
+        BlockCompressedInputStream bcis = null;
+        if (filename.startsWith("http:") || filename.startsWith("https:") || filename.startsWith("ftp:")) {
+            bcis = new BlockCompressedInputStream(new URL(filename + ".tbi"));
+        } else {
+            bcis = new BlockCompressedInputStream(new File(filename + ".tbi"));
+        }
+        readIndex(bcis);
+    }
+
+    /**
+     * Read one line from the data file.
+     */
+    public String readLine() throws IOException {
+        return readNextLine(blockCompressedInputStream);
+    }
+
+    private int chr2tid(final String chr) {
+        if (chr2tid.containsKey(chr)) return chr2tid.get(chr);
+        else return -1;
+    }
+
+    /**
+     * Parse a region in the format of "chr1", "chr1:100" or "chr1:100-1000"
+     *
+     * @param reg Region string
+     * @return An array where the three elements are sequence_id,
+     *         region_begin and region_end. On failure, sequence_id==-1.
+     */
+    public int[] parseReg(final String reg) { // TODO FIXME: NOT working when the sequence name contains : or -.
+        String chr;
+        int colon, hyphen;
+        int[] ret = new int[3];
+        colon = reg.indexOf(':');
+        hyphen = reg.indexOf('-');
+        chr = colon >= 0 ? reg.substring(0, colon) : reg;
+        ret[1] = colon >= 0 ? Integer.parseInt(reg.substring(colon + 1, hyphen)) - 1 : 0;
+        ret[2] = hyphen >= 0 ? Integer.parseInt(reg.substring(hyphen + 1)) : 0x7fffffff;
+        ret[0] = chr2tid(chr);
+        return ret;
+    }
+
+    private TIntv getIntv(final String s) {
+        TIntv intv = new TIntv();
+        int col = 0, end = 0, beg = 0;
+        while ((end = s.indexOf('\t', beg)) >= 0) {
+            ++col;
+            if (col == seqCol) {
+                intv.tid = chr2tid(s.substring(beg, end));
+            } else if (col == begCol) {
+                intv.beg = intv.end = Integer.parseInt(s.substring(beg, end));
+                if ((format & 0x10000) != 0) ++intv.end;
+                else --intv.beg;
+            } else { // TODO FIXME: SAM/VCF supports are not tested yet
+                if ((format & 0xffff) == 0) { // generic
+                    if (col == endCol)
+                        intv.end = Integer.parseInt(s.substring(beg, end));
+                } else if ((format & 0xffff) == 1) { // SAM
+                    if (col == 6) { // CIGAR
+                        int l = 0, i, j;
+                        String cigar = s.substring(beg, end);
+                        for (i = j = 0; i < cigar.length(); ++i) {
+                            if (cigar.charAt(i) > '9') {
+                                int op = cigar.charAt(i);
+                                if (op == 'M' || op == 'D' || op == 'N')
+                                    l += Integer.parseInt(cigar.substring(j, i));
+                            }
+                        }
+                        intv.end = intv.beg + l;
+                    }
+                } else if ((format & 0xffff) == 2) { // VCF
+                    if (col == 5) {
+                        String alt = s.substring(beg, end);
+                        int i, max = 1;
+                        for (i = 0; i < alt.length(); ++i) {
+                            if (alt.charAt(i) == 'D') { // deletion
+                                int j;
+                                for (j = i + 1; j < alt.length() && alt.charAt(j) >= '0' && alt.charAt(j) <= '9'; ++j) ;
+                                int l = Integer.parseInt(alt.substring(i + 1, j));
+                                if (max < l) max = l;
+                                i = j - 1;
+                            }
+                        }
+                        intv.end = intv.beg + max;
+                    }
+                }
+            }
+            beg = end + 1;
+        }
+        return intv;
+    }
+
+    public class TabixLineReader implements LineReader {
+        private int i, n_seeks;
+        private int tid, beg, end;
+        private TPair64[] off;
+        private long curr_off;
+        private boolean iseof;
+
+        public TabixLineReader(final int _tid, final int _beg, final int _end, final TPair64[] _off) {
+            i = -1;
+            n_seeks = 0;
+            curr_off = 0;
+            iseof = false;
+            off = _off;
+            tid = _tid;
+            beg = _beg;
+            end = _end;
+        }
+
+        public String readLine() throws IOException {
+            if (iseof) return null;
+            for (; ;) {
+                if (curr_off == 0 || !less64(curr_off, off[i].v)) { // then jump to the nextLine chunk
+                    if (i == off.length - 1) break; // no more chunks
+                    if (i >= 0) assert (curr_off == off[i].v); // otherwise bug
+                    if (i < 0 || off[i].v != off[i + 1].u) { // not adjacent chunks; then seek
+                        blockCompressedInputStream.seek(off[i + 1].u);
+                        curr_off = blockCompressedInputStream.getFilePointer();
+                        ++n_seeks;
+                    }
+                    ++i;
+                }
+                String s;
+                if ((s = readNextLine(blockCompressedInputStream)) != null) {
+                    TIntv intv;
+                    char[] str = s.toCharArray();
+                    curr_off = blockCompressedInputStream.getFilePointer();
+                    if (str.length == 0 || str[0] == metaChar) continue;
+                    intv = getIntv(s);
+                    if (intv.tid != tid || intv.beg >= end) break; // no need to proceed
+                    else if (intv.end > beg && intv.beg < end) return s; // overlap; return
+                } else break; // end of file
+            }
+            iseof = true;
+            return null;
+        }
+
+        public void close() throws IOException {
+            blockCompressedInputStream.close();
+        }
+    }
+
+    private TabixLineReader query(final int tid, final int beg, final int end) {
+        TPair64[] off;
+        TPair64[] chunks;
+        long min_off;
+        TIndex idx = mIndex[tid];
+        int[] bins = new int[MAX_BIN];
+        int i, l, n_off, n_bins = reg2bins(beg, end, bins);
+        min_off = (beg >> TAD_LIDX_SHIFT >= idx.l.length) ? 0 : idx.l[beg >> TAD_LIDX_SHIFT];
+        for (i = n_off = 0; i < n_bins; ++i) {
+            if ((chunks = idx.b.get(bins[i])) != null)
+                n_off += chunks.length;
+        }
+        if (n_off == 0) {
+            return null;
+        }
+
+        off = new TPair64[n_off];
+        for (i = n_off = 0; i < n_bins; ++i)
+            if ((chunks = idx.b.get(bins[i])) != null)
+                for (int j = 0; j < chunks.length; ++j)
+                    if (less64(min_off, chunks[j].v))
+                        off[n_off++] = new TPair64(chunks[j]);
+        Arrays.sort(off, 0, n_off);
+        // resolve completely contained adjacent blocks
+        for (i = 1, l = 0; i < n_off; ++i) {
+            if (less64(off[l].v, off[i].v)) {
+                ++l;
+                off[l].u = off[i].u;
+                off[l].v = off[i].v;
+            }
+        }
+
+        n_off = l + 1;
+        // resolve overlaps between adjacent blocks; this may happen due to the merge in indexing
+        for (i = 1; i < n_off; ++i)
+            if (!less64(off[i - 1].v, off[i].u)) off[i - 1].v = off[i].u;
+        // merge adjacent blocks
+        for (i = 1, l = 0; i < n_off; ++i) {
+            if (off[l].v >> 16 == off[i].u >> 16) off[l].v = off[i].v;
+            else {
+                ++l;
+                off[l].u = off[i].u;
+                off[l].v = off[i].v;
+            }
+        }
+        n_off = l + 1;
+        // return
+        TPair64[] ret = new TPair64[n_off];
+        for (i = 0; i < n_off; ++i) ret[i] = new TPair64(off[i].u, off[i].v); // in C, this is inefficient
+        return new TabixLineReader(tid, beg, end, ret);
+    }
+
+    public TabixLineReader query(final String reg) {
+        int[] x = parseReg(reg);
+        return query(x[0], x[1], x[2]);
+    }
+
+    public LineReader iterate() {
+        return this;
+        //return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    /**
+     * NOTE:  convert from UCSC coords to 1 base
+     *
+     * @param chr
+     * @param start
+     * @param end
+     * @return
+     */
+    public TabixLineReader query(final String chr, int start, int end) {
+        int tid = chr2tid(chr);
+        if (tid >= 0) {
+            // Added by JTR to prevent index out of bounds exceptions
+            int adjustedStart = Math.max(0, start-1);
+            return query(chr2tid(chr), adjustedStart, end);
+        } else {
+            return null;
+        }
+    }
+
+    public void close() throws IOException {
+        blockCompressedInputStream.close();
+    }
+
+
+    public static boolean isTabix(String path) {
+        if (!path.endsWith("gz")) {
+            return false;
+        }
+
+        BlockCompressedInputStream is = null;
+        try {
+            if (path.startsWith("http:") || path.startsWith("https:") || path.startsWith("ftp:")) {
+                is = new BlockCompressedInputStream(new URL(path + ".tbi"));
+            } else {
+                is = new BlockCompressedInputStream(new File(path + ".tbi"));
+            }
+
+            if (is == null) {
+                return false;
+            }
+            byte[] bytes = new byte[4];
+            is.read(bytes);
+            return (char) bytes[0] == 'T' && (char) bytes[1] == 'B';
+        } catch (IOException e) {
+            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+            return false;
+        }
+        finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+                }
+            }
+        }
+    }
+
+
+    public static void main(String[] args) {
+        args = new String[2];
+        args[0] = "/Users/jrobinso/projects/Version_1.5_rc2/test/data/CEU.SRP000032.2010_03.genotypes.vcf.gz";
+        args[1] = "1:58003474-58013474";
+        if (args.length < 1) {
+            System.out.println("Usage: java -cp .:sam.jar TabixReader <in.gz> [region]");
+            System.exit(1);
+        }
+        System.out.println(isTabix(args[0]));
+        try {
+            TabixReader tr = new TabixReader(args[0]);
+
+
+            String s;
+            if (args.length == 1) { // no region is specified; print the whole file
+                while ((s = tr.readLine()) != null)
+                    System.out.println(s);
+            } else { // a region is specified; random access
+                TabixLineReader iter = tr.query(args[1]); // get the iterator
+                while ((s = iter.readLine()) != null)
+                    System.out.println(s);
+            }
+        } catch (IOException e) {
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/org/broad/tribble/util/AsciiLineReader.java b/src/org/broad/tribble/util/AsciiLineReader.java
new file mode 100644
index 0000000..fc713b8
--- /dev/null
+++ b/src/org/broad/tribble/util/AsciiLineReader.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+package org.broad.tribble.util;
+
+import java.io.*;
+
+/**
+ * @author jrobinso
+ */
+public class AsciiLineReader implements LineReader {
+    private static final byte LINEFEED = (byte) ('\n' & 0xff);
+    private static final byte CARRIAGE_RETURN = (byte) ('\r' & 0xff);
+
+
+    InputStream is;
+    byte[] buffer;
+    int nextChar;
+    int nChars;
+    char[] lineBuffer;
+    long lineNumber = 0;
+    long position;
+
+    public AsciiLineReader(InputStream is) {
+        this(is, 512000);
+    }
+
+    public AsciiLineReader(InputStream is, int bufferSize) {
+        this.is = is;
+        buffer = new byte[bufferSize];
+        nextChar = nChars = 0;
+
+        // Allocate this only once, even though it is essentially a local variable of
+        // readLine.  This makes a huge difference in performance
+        lineBuffer = new char[10000];
+    }
+
+    public long getPosition() {
+        return position;
+    }
+
+    public void skip(long nBytes) throws IOException {
+        is.skip(nBytes);
+    }
+
+    /**
+     * Read a line of text.  A line is considered to be terminated by any one
+     * of a line feed ('\n'), a carriage return ('\r'), or a carriage return
+     * followed immediately by a linefeed.
+     *
+     * @return A String containing the contents of the line or null if the
+     *         end of the stream has been reached
+     */
+    public String readLine() throws IOException {
+        int linePosition = 0;
+
+        while (true) {
+            if (nChars == -1) {
+                return null;
+            }
+
+            // Refill buffer if neccessary
+            if (nextChar == nChars) {
+                fill();
+                if (nextChar == nChars || nChars == -1) {
+                    // eof reached.  Return the last line, or null if this is a new line
+                    if (linePosition > 0) {
+                        lineNumber++;
+                        position += linePosition;
+                        return new String(lineBuffer, 0, linePosition);
+                    } else {
+                        return null;
+                    }
+                }
+            }
+
+
+            char c = (char) (buffer[nextChar++] & 0xFF);
+            if (c == LINEFEED || c == CARRIAGE_RETURN) {
+
+                // + 1 for the terminator
+                position += linePosition + 1;
+
+                if (c == CARRIAGE_RETURN && peek() == LINEFEED) {
+                    nextChar++; // <= skip the trailing \n in case of \r\n termination
+                    position++;
+                }
+                lineNumber++;
+
+                return new String(lineBuffer, 0, linePosition);
+            } else {
+                // Expand line buffer size if neccessary.  Reserve at least 2 characters
+                // for potential line-terminators in return string
+
+                if (linePosition > (lineBuffer.length - 3)) {
+                    char[] temp = new char[lineBuffer.length + 1000];
+                    System.arraycopy(lineBuffer, 0, temp, 0, lineBuffer.length);
+                    lineBuffer = temp;
+                }
+
+                lineBuffer[linePosition++] = c;
+            }
+        }
+    }
+
+    /**
+     * Peek ahead one character, filling from the underlying stream if neccessary.
+     *
+     * @return
+     * @throws java.io.IOException
+     */
+    private char peek() throws IOException {
+        // Refill buffer if neccessary
+        if (nextChar == nChars) {
+            fill();
+            if (nextChar == nChars) {
+                // eof reached.
+                return 0;
+            }
+        }
+        return (char) buffer[nextChar];
+
+    }
+
+    private void fill() throws IOException {
+        nChars = is.read(buffer);
+        nextChar = 0;
+    }
+
+    public void close() {
+        try {
+            is.close();
+            lineNumber = 0;
+
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    public long getCurrentLineNumber() {
+        return lineNumber;
+    }
+
+    public static void main(String[] args) throws Exception {
+        File testFile = new File("test/data/HindForGISTIC.hg16.cn");
+        long t0, lineCount, dt;
+        double rate;
+
+        for (int i = 0; i < 3; i++) {
+            BufferedReader reader2 = new BufferedReader(new FileReader(testFile));
+            t0 = System.currentTimeMillis();
+            lineCount = 0;
+            while (reader2.readLine() != null) {
+                lineCount++;
+            }
+            dt = System.currentTimeMillis() - t0;
+            rate = ((double) lineCount) / dt;
+            System.out.println("BR: " + lineCount + " lines read.  Rate = " + rate + " lines per second.   DT = " + dt);
+            reader2.close();
+
+            AsciiLineReader reader = new AsciiLineReader(new FileInputStream(testFile));
+            t0 = System.currentTimeMillis();
+            lineCount = 0;
+            while (reader.readLine() != null) {
+                lineCount++;
+            }
+            dt = System.currentTimeMillis() - t0;
+            rate = ((double) lineCount) / dt;
+            System.out.println("AR: " + lineCount + " lines read.  Rate = " + rate + " lines per second.     DT = " + dt);
+            reader.close();
+        }
+
+
+    }
+}
\ No newline at end of file
diff --git a/src/org/broad/tribble/util/CloseableTribbleIterator.java b/src/org/broad/tribble/util/CloseableTribbleIterator.java
new file mode 100644
index 0000000..630f0b4
--- /dev/null
+++ b/src/org/broad/tribble/util/CloseableTribbleIterator.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2009-2010 by The Broad Institute, Inc.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble.util;
+
+import net.sf.samtools.util.CloseableIterator;
+import org.broad.tribble.Feature;
+
+import java.util.Iterator;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: aaron
+ * Date: May 10, 2010
+ * Time: 12:14:35 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public interface CloseableTribbleIterator<T extends Feature> extends Iterator<T>, Iterable<T>, CloseableIterator<T> {
+}
\ No newline at end of file
diff --git a/src/org/broad/tribble/util/HttpUtils.java b/src/org/broad/tribble/util/HttpUtils.java
new file mode 100644
index 0000000..40bd681
--- /dev/null
+++ b/src/org/broad/tribble/util/HttpUtils.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble.util;
+
+import org.apache.log4j.Logger;
+
+import java.io.IOException;
+import java.net.*;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: Sep 23, 2009
+ * Time: 5:51:21 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class HttpUtils {
+
+    private static Logger log = Logger.getLogger(HttpUtils.class);
+
+    /**
+     * Proxy settings (can be null)
+     * TODO -- making this a global static is very ugly, and should be fixed, but doing so will require some IGV refactoring
+     */
+    private static ProxySettings proxySettings = null;
+
+    public static String getHeaderField(URL url, String name) {
+
+        URLConnection conn = null;
+        try {
+            // Create a URLConnection object for a URL
+            conn = openConnection(url);
+            conn.setReadTimeout(5000);
+            return conn.getHeaderField(name);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+        finally {
+            if (conn != null && conn instanceof HttpURLConnection) {
+                ((HttpURLConnection) conn).disconnect();
+            }
+        }
+    }
+
+    public static long getContentLength(URL url) {
+
+        String contentLengthString = HttpUtils.getHeaderField(url, "Content-length");
+        if (contentLengthString == null) {
+            return -1;
+        } else {
+            try {
+                return Long.parseLong(contentLengthString);
+            }
+            catch (NumberFormatException e) {
+                log.error("Error parsing content length string: " + contentLengthString);
+                return -1;
+            }
+        }
+    }
+
+    public static boolean resourceAvailable(URL url) {
+        URLConnection conn = null;
+        try {
+            // Create a URLConnection object for a URL
+            conn = openConnection(url);
+            conn.setReadTimeout(5000);
+            if (conn.getHeaderField("ETag") != null) {
+                return true;
+            } else {
+                return false;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+        finally {
+            if (conn != null && conn instanceof HttpURLConnection) {
+                ((HttpURLConnection) conn).disconnect();
+            }
+        }
+    }
+
+
+    /**
+     * Wraps url.openConnection(),  and adds proxy authentication if required.
+     *
+     * @param url
+     * @return
+     * @throws java.io.IOException
+     */
+    public static HttpURLConnection openConnection(URL url) throws IOException {
+
+        if (proxySettings != null && proxySettings.useProxy && proxySettings.proxyHost != null && proxySettings.proxyPort > 0) {
+            Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxySettings.proxyHost, proxySettings.proxyPort));
+            HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy);
+            if (proxySettings.auth && proxySettings.user != null && proxySettings.pw != null) {
+                String encodedUserPwd = base64Encode(proxySettings.user + ":" + proxySettings.pw);
+                conn.setRequestProperty("Proxy-Authorization", "Basic " + encodedUserPwd);
+            }
+            return conn;
+
+        } else {
+            return (HttpURLConnection) url.openConnection();
+        }
+    }
+
+
+    // TODO -- replace use of sun package
+    public static String base64Encode(String str) {
+        sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
+        byte[] bytes = str.getBytes();
+        return encoder.encode(bytes);
+
+    }
+
+    // TODO -- replace use of sun package
+    public static String base64Decode(String str) {
+        try {
+            return new String((new sun.misc.BASE64Decoder()).decodeBuffer(str));
+        } catch (IOException e) {
+            log.error("Error decoding string: " + str, e);
+            return str;
+        }
+    }
+
+
+    public static void setProxySettings(ProxySettings ps) {
+        proxySettings = ps;
+    }
+
+
+    public static class ProxySettings {
+        boolean auth = false;
+        String user;
+        String pw;
+        boolean useProxy;
+        String proxyHost;
+        int proxyPort = -1;
+
+        public ProxySettings(boolean useProxy, String user, String pw, boolean auth, String proxyHost, int proxyPort) {
+            this.auth = auth;
+            this.proxyHost = proxyHost;
+            this.proxyPort = proxyPort;
+            this.pw = pw;
+            this.useProxy = useProxy;
+            this.user = user;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/org/broad/tribble/util/LineReader.java b/src/org/broad/tribble/util/LineReader.java
new file mode 100644
index 0000000..37fe7cf
--- /dev/null
+++ b/src/org/broad/tribble/util/LineReader.java
@@ -0,0 +1,17 @@
+package org.broad.tribble.util;
+
+import java.io.IOException;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: May 17, 2010
+ * Time: 5:09:39 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public interface LineReader {
+
+    public String readLine() throws IOException;
+
+    public void close() throws IOException;
+}
\ No newline at end of file
diff --git a/src/org/broad/tribble/util/LittleEndianInputStream.java b/src/org/broad/tribble/util/LittleEndianInputStream.java
new file mode 100644
index 0000000..7a68275
--- /dev/null
+++ b/src/org/broad/tribble/util/LittleEndianInputStream.java
@@ -0,0 +1,128 @@
+/*
+* Adapted from example code in
+* Title: Hardcore Java
+* Title: Java I/O
+* Second Edition: May 2006
+* ISBN 10: 0-596-52750-0
+* ISBN 13: 9780596527501
+*
+* http://www.javafaq.nu/java-example-code-1078.html
+*
+*/
+package org.broad.tribble.util;
+
+import java.io.*;
+
+
+public class LittleEndianInputStream extends FilterInputStream {
+
+    public LittleEndianInputStream(InputStream in) {
+        super(in);
+    }
+
+    public boolean readBoolean() throws IOException {
+        int bool = in.read();
+        if (bool == -1) throw new EOFException();
+        return (bool != 0);
+    }
+
+    public byte readByte(int b) throws IOException {
+        int temp = in.read();
+        if (temp == -1) throw new EOFException();
+        return (byte) temp;
+    }
+
+    public int readUnsignedByte() throws IOException {
+        int temp = in.read();
+        if (temp == -1) throw new EOFException();
+        return temp;
+    }
+
+    public short readShort() throws IOException {
+        int byte1 = in.read();
+        int byte2 = in.read();
+        // only need to test last byte read
+        // if byte1 is -1 so is byte2
+        if (byte2 == -1) throw new EOFException();
+        return (short) (((byte2 << 24) >>> 16) + (byte1 << 24) >>> 24);
+    }
+
+    public int readUnsignedShort() throws IOException {
+        int byte1 = in.read();
+        int byte2 = in.read();
+        if (byte2 == -1) throw new EOFException();
+        return ((byte2 << 24) >> 16) + ((byte1 << 24) >> 24);
+    }
+
+    public char readChar() throws IOException {
+        int byte1 = in.read();
+        int byte2 = in.read();
+        if (byte2 == -1) throw new EOFException();
+        return (char) (((byte2 << 24) >>> 16) + ((byte1 << 24) >>> 24));
+    }
+
+    public int readInt() throws IOException {
+
+        int byte1 = in.read();
+        int byte2 = in.read();
+        int byte3 = in.read();
+        int byte4 = in.read();
+        if (byte4 == -1) {
+            throw new EOFException();
+        }
+        return (byte4 << 24)
+                + ((byte3 << 24) >>> 8)
+                + ((byte2 << 24) >>> 16)
+                + ((byte1 << 24) >>> 24);
+
+    }
+
+    public long readLong() throws IOException {
+
+        long byte1 = in.read();
+        long byte2 = in.read();
+        long byte3 = in.read();
+        long byte4 = in.read();
+        long byte5 = in.read();
+        long byte6 = in.read();
+        long byte7 = in.read();
+        long byte8 = in.read();
+        if (byte8 == -1) {
+            throw new EOFException();
+        }
+        return (byte8 << 56)
+                + ((byte7 << 56) >>> 8)
+                + ((byte6 << 56) >>> 16)
+                + ((byte5 << 56) >>> 24)
+                + ((byte4 << 56) >>> 32)
+                + ((byte3 << 56) >>> 40)
+                + ((byte2 << 56) >>> 48)
+                + ((byte1 << 56) >>> 56);
+
+    }
+
+    public final double readDouble() throws IOException {
+        return Double.longBitsToDouble(this.readLong());
+    }
+
+    public final float readFloat() throws IOException {
+        return Float.intBitsToFloat(this.readInt());
+    }
+
+    /**
+     * Read a null terminated byte array and return result as a string
+     *
+     * @return
+     * @throws IOException
+     */
+
+    public String readString() throws IOException {
+        ByteArrayOutputStream bis = new ByteArrayOutputStream(100);
+        byte b ;
+        while ((b = (byte) in.read()) != 0) {
+            bis.write(b);
+        }
+        return new String(bis.toByteArray());
+    }
+
+}
diff --git a/src/org/broad/tribble/util/LittleEndianOutputStream.java b/src/org/broad/tribble/util/LittleEndianOutputStream.java
new file mode 100644
index 0000000..41a1a91
--- /dev/null
+++ b/src/org/broad/tribble/util/LittleEndianOutputStream.java
@@ -0,0 +1,111 @@
+/*
+* Adapted from example code in
+* Title: Hardcore Java
+* Title: Java I/O
+* Second Edition: May 2006
+* ISBN 10: 0-596-52750-0
+* ISBN 13: 9780596527501
+*
+* http://www.javafaq.nu/java-example-code-1078.html
+*
+*/
+
+package org.broad.tribble.util;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+public final class LittleEndianOutputStream extends FilterOutputStream {
+
+    protected int written;
+
+    public LittleEndianOutputStream(OutputStream out) {
+        super(out);
+    }
+
+    public void write(int b) throws IOException {
+        out.write(b);
+        written++;
+    }
+
+    public void write(byte[] data, int offset, int length)
+            throws IOException {
+        out.write(data, offset, length);
+        written += length;
+    }
+
+    public void writeBoolean(boolean b) throws IOException {
+        if (b) this.write(1);
+        else this.write(0);
+    }
+
+    public void writeByte(int b) throws IOException {
+        out.write(b);
+        written++;
+    }
+
+    public void writeShort(int s) throws IOException {
+        out.write(s & 0xFF);
+        out.write((s >>> 8) & 0xFF);
+        written += 2;
+    }
+
+    public void writeChar(int c) throws IOException {
+        out.write(c & 0xFF);
+        out.write((c >>> 8) & 0xFF);
+        written += 2;
+    }
+
+    public void writeInt(int i) throws IOException {
+
+        out.write(i & 0xFF);
+        out.write((i >>> 8) & 0xFF);
+        out.write((i >>> 16) & 0xFF);
+        out.write((i >>> 24) & 0xFF);
+        written += 4;
+
+    }
+
+    public void writeLong(long l) throws IOException {
+
+        out.write((int) l & 0xFF);
+        out.write((int) (l >>> 8) & 0xFF);
+        out.write((int) (l >>> 16) & 0xFF);
+        out.write((int) (l >>> 24) & 0xFF);
+        out.write((int) (l >>> 32) & 0xFF);
+        out.write((int) (l >>> 40) & 0xFF);
+        out.write((int) (l >>> 48) & 0xFF);
+        out.write((int) (l >>> 56) & 0xFF);
+        written += 8;
+
+    }
+
+    public final void writeFloat(float f) throws IOException {
+        this.writeInt(Float.floatToIntBits(f));
+    }
+
+    public final void writeDouble(double d) throws IOException {
+        this.writeLong(Double.doubleToLongBits(d));
+    }
+
+    public void writeBytes(String s) throws IOException {
+        int length = s.length();
+        for (int i = 0; i < length; i++) {
+            out.write((byte) s.charAt(i));
+        }
+        written += length;
+    }
+
+    /**
+     * Srite a string as a null terminated byte array.
+     *
+     * @param s
+     * @throws IOException
+     */
+    public void writeString(String s) throws IOException {
+        writeBytes(s);
+        out.write((byte) 0);
+    }
+}// end LittleEndianOutputStream
diff --git a/src/org/broad/tribble/util/ParsingUtils.java b/src/org/broad/tribble/util/ParsingUtils.java
new file mode 100644
index 0000000..2d95fa5
--- /dev/null
+++ b/src/org/broad/tribble/util/ParsingUtils.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+package org.broad.tribble.util;
+
+import org.apache.log4j.Logger;
+
+import java.io.*;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.*;
+import java.util.zip.GZIPInputStream;
+
+/** @author jrobinso */
+public class ParsingUtils {
+
+    private static Logger log = Logger.getLogger(ParsingUtils.class);
+
+    public static BufferedReader openBufferedReader(String path)
+            throws IOException {
+        InputStream stream = openInputStream(path);
+        return new BufferedReader(new InputStreamReader(stream));
+    }
+
+
+    public static AsciiLineReader openAsciiReader(String path)
+            throws IOException {
+        InputStream stream = openInputStream(path);
+        return new AsciiLineReader(stream);
+
+    }
+
+
+    public static InputStream openInputStream(String path)
+            throws IOException {
+
+        InputStream inputStream = null;
+        if (path.startsWith("ftp:")) {
+            // TODO -- throw an appropriate exception
+            throw new RuntimeException("FTP streams not supported.");
+        }
+        if (path.startsWith("http:") || path.startsWith("https:") || path.startsWith("file:")) {
+            URL url = new URL(path);
+            URLConnection connection = url.openConnection();
+            inputStream = connection.getInputStream();
+        } else {
+            File file = new File(path);
+            inputStream = new FileInputStream(file);
+        }
+
+        if (path.endsWith("gz")) {
+            return new GZIPInputStream(inputStream);
+        } else {
+            return inputStream;
+        }
+
+    }
+
+
+    public static int estimateLineCount(String filename) {
+
+        AsciiLineReader reader = null;
+        try {
+            long fileLength = 0;
+            //TODO - ftp
+            if (filename.startsWith("http:") || filename.startsWith("https:")) {
+                URL url = new URL(filename);
+                fileLength = Long.parseLong(HttpUtils.getHeaderField(url, "Content-length"));
+            } else {
+                fileLength = (new File(filename)).length();
+            }
+
+
+            reader = openAsciiReader(filename);
+            String nextLine;
+            int lines = 0;
+            while ((nextLine = reader.readLine()) != null & lines < 100) {
+                lines++;
+            }
+            double bytesPerLine = ((double) reader.getPosition()) / lines;
+            int nLines = (int) (fileLength / bytesPerLine);
+            return nLines;
+
+        } catch (Exception e) {
+            log.error("Error estimating line count", e);
+            return 1000;
+        } finally {
+            reader.close();
+        }
+
+    }
+    /**
+     * join an array of strings given a seperator
+     * @param separator the string to insert between each array element
+     * @param strings the array of strings
+     * @return a string, which is the joining of all array values with the separator
+     */
+    public static String join(String separator, String[] strings) {
+        return join(separator, strings, 0, strings.length);
+    }
+
+    public static String join(String separator, String[] strings, int start, int end) {
+        if ((end - start) == 0) {
+            return "";
+        }
+        StringBuilder ret = new StringBuilder(strings[start]);
+        for (int i = start + 1; i < end; ++i) {
+            ret.append(separator);
+            ret.append(strings[i]);
+        }
+        return ret.toString();
+    }
+
+
+    /**
+     * Split the string into tokesn separated by the given delimiter.  Profiling has
+     * revealed that the standard string.split() method typically takes > 1/2
+     * the total time when used for parsing ascii files.
+     *
+     * @param aString the string to split
+     * @param tokens  an array to hold the parsed tokens
+     * @param delim   character that delimits tokens
+     * @return the number of tokens parsed
+     */
+    public static int split(String aString, String[] tokens, char delim) {
+
+        int maxTokens = tokens.length;
+        int nTokens = 0;
+        int start = 0;
+        int end = aString.indexOf(delim);
+        if (end < 0) {
+            tokens[nTokens++] = aString;
+            return nTokens;
+        }
+        while ((end > 0) && (nTokens < maxTokens)) {
+            //tokens[nTokens++] = new String(aString.toCharArray(), start, end-start); //  aString.substring(start, end);
+            tokens[nTokens++] = aString.substring(start, end);
+            start = end + 1;
+            end = aString.indexOf(delim, start);
+
+        }
+
+        // Add the trailing string
+        if (nTokens < maxTokens) {
+            String trailingString = aString.substring(start);
+            tokens[nTokens++] = trailingString;
+        }
+        return nTokens;
+    }
+
+
+
+    // trim a string for the given character (i.e. not just whitespace)
+    public static String trim(String str, char ch) {
+        char[] array = str.toCharArray();
+        int start = 0;
+        while (start < array.length && array[start] == ch)
+            start++;
+
+        int end = array.length - 1;
+        while (end > start && array[end] == ch)
+            end--;
+
+        return str.substring(start, end + 1);
+    }
+
+
+    /**
+     * Split the string into tokesn separated by tab or space.  This method
+     * was added so support wig and bed files, which apparently accept
+     * either.
+     *
+     * @param aString the string to split
+     * @param tokens  an array to hold the parsed tokens
+     * @return the number of tokens parsed
+     */
+    public static int splitWhitespace(String aString, String[] tokens) {
+
+        int maxTokens = tokens.length;
+        int nTokens = 0;
+        int start = 0;
+        int tabEnd = aString.indexOf('\t');
+        int spaceEnd = aString.indexOf(' ');
+        int end = tabEnd < 0 ? spaceEnd : spaceEnd < 0 ? tabEnd : Math.min(spaceEnd, tabEnd);
+        while  ((end > 0) && (nTokens < maxTokens)) {
+            //tokens[nTokens++] = new String(aString.toCharArray(), start, end-start); //  aString.substring(start, end);
+            tokens[nTokens++] = aString.substring(start, end);
+
+            start = end + 1;
+            // Gobble up any whitespace before next token -- don't gobble tabs, consecutive tabs => empty cell
+            while (start < aString.length() && aString.charAt(start) == ' ') {
+                start++;
+            }
+
+            tabEnd = aString.indexOf('\t', start);
+            spaceEnd = aString.indexOf(' ', start);
+            end = tabEnd < 0 ? spaceEnd : spaceEnd < 0 ? tabEnd : Math.min(spaceEnd, tabEnd);
+
+        }
+
+        // Add the trailing string
+        if (nTokens < maxTokens) {
+            String trailingString = aString.substring(start);
+            tokens[nTokens++] = trailingString;
+        }
+        return nTokens;
+    }
+
+
+    /**
+     * Method description
+     *
+     * @param file
+     *
+     * @return
+     */
+    public static List<String> loadRegions(File file) {
+        try {
+            FileInputStream fileInput = new FileInputStream(file);
+            BufferedReader reader = new BufferedReader(new InputStreamReader(fileInput));
+            String nextLine;
+            List<String> features = new ArrayList<String>();
+            while ((nextLine = reader.readLine()) != null && (nextLine.trim().length() > 0)) {
+                try {
+                    if (nextLine.startsWith("chr")) {
+                        String[] tokens = nextLine.split("\t");
+                        String region = tokens[0] + ":" + tokens[1] + "-" + tokens[2];
+                        features.add(region);
+                    }
+                } catch (NumberFormatException e) {
+                    log.error("Error parsing numer in line: " + nextLine);
+                }
+            }
+
+            reader.close();
+            return features;
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+
+    /*
+    * Legacy conversion for human genome files.
+    *
+    * @param chr
+    * @return
+    */
+    /** Method description */
+    static Set<String> legacyGenomes = new HashSet();
+
+    static {
+        legacyGenomes.add("S._cerevisiae");
+        legacyGenomes.add("Yeast_S._pombe");
+        legacyGenomes.add("Chicken_galGal3");
+    }
+
+    /** Method description */
+    private static Map<String, String> humanChrLookupTable = new HashMap(100);
+    private static Map<String, String> chrLookupTable = new HashMap(100);
+
+    // Pre seed chr table with "1,2,3,  etc"
+
+    static {
+        for (int i = 0; i < 23; i++) {
+            humanChrLookupTable.put(String.valueOf(i), "chr" + i);
+            //chrLookupTable.put("Chr" + i, "chr" + i);
+        }
+        humanChrLookupTable.put("X", "chrX");
+        humanChrLookupTable.put("Y", "chrY");
+        humanChrLookupTable.put("M", "chrM");
+        humanChrLookupTable.put("x", "chrX");
+        humanChrLookupTable.put("y", "chrY");
+        humanChrLookupTable.put("m", "chrM");
+    }
+
+
+    public static String convertChrString(String genomeId, String str) {
+
+        if (str == null) {
+            return null;
+        }
+
+        String chr = str;
+
+        // Special legacy rules for UCSC human & mouse
+        if (genomeId != null && genomeId.startsWith("hg") || genomeId.startsWith("mm")) {
+            if (genomeId.startsWith("hg")) {
+                str = str.replace("23", "X");
+                str = str.replace("24", "Y");
+            } else if (genomeId.startsWith("mm")) {
+                str = str.replace("20", "X");
+                str = str.replace("21", "Y");
+            }
+            chr = humanChrLookupTable.get(chr);
+            if (chr == null) {
+                chr = str;
+                humanChrLookupTable.put(str, chr);
+            }
+        }
+
+        // All other genomes.  Lookup table is used to prevent keeping zillions of identical strings
+        else {
+            chr = chrLookupTable.get(chr);
+            if (chr == null) {
+                chr = str;
+                chrLookupTable.put(str, chr);
+            }
+
+        }
+
+        return chr;
+
+    }
+
+
+}
\ No newline at end of file
diff --git a/src/org/broad/tribble/util/SeekableFileStream.java b/src/org/broad/tribble/util/SeekableFileStream.java
new file mode 100644
index 0000000..89e5abd
--- /dev/null
+++ b/src/org/broad/tribble/util/SeekableFileStream.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.tribble.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ * @author jrobinso
+ */
+public class SeekableFileStream extends SeekableStream {
+
+    File file;
+    FileInputStream fis;
+
+    public SeekableFileStream(File file) throws FileNotFoundException {
+        this.file = file;
+        fis = new FileInputStream(file);
+    }
+
+    public void seek(long position) throws IOException {
+        fis.getChannel().position(position);
+    }
+
+    public long position() throws IOException {
+        return fis.getChannel().position();
+    }
+
+    @Override
+    public long skip(long n) throws IOException {
+        return fis.skip(n);
+    }
+
+    @Override
+    public int read(byte[] buffer, int offset, int length) throws IOException {
+        return fis.read(buffer, offset, length);
+    }
+
+    @Override
+    public int read() throws IOException {
+        return fis.read();
+    }
+
+    @Override
+    public int read(byte[] b) throws IOException {
+        return fis.read(b);
+    }
+
+    @Override
+    public int available() throws IOException {
+        return fis.available();
+    }
+
+    @Override
+    public void mark(int readlimit) {
+        fis.mark(readlimit);
+    }
+
+    @Override
+    public boolean markSupported() {
+        return fis.markSupported();
+    }
+
+    @Override
+    public void reset() throws IOException {
+        fis.reset();
+    }
+
+    @Override
+    public void close() throws IOException {
+        fis.close();
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/org/broad/tribble/util/SeekableHTTPStream.java b/src/org/broad/tribble/util/SeekableHTTPStream.java
new file mode 100644
index 0000000..a8d2bae
--- /dev/null
+++ b/src/org/broad/tribble/util/SeekableHTTPStream.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2007-2009 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+ * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+ * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+ * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+ * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+ * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+ * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+ * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+ * FOREGOING.
+ */
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.broad.tribble.util;
+
+import org.apache.log4j.Logger;
+
+import java.io.BufferedInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author jrobinso
+ */
+public class SeekableHTTPStream extends SeekableStream {
+
+    static Logger log = Logger.getLogger(SeekableHTTPStream.class);
+
+    private long position = 0;
+    private long contentLength = -1;
+    private URL url;
+
+    public SeekableHTTPStream(URL url) {
+        this.url = url;
+
+        if (log.isDebugEnabled()) {
+            log.debug("Creating SeekableHTTPStream for: " + url);
+        }
+
+        // Try to get the file length
+        String contentLengthString = HttpUtils.getHeaderField(url, "Content-Length");
+        if (contentLengthString != null) {
+            try {
+                contentLength = Long.parseLong(contentLengthString);
+            }
+            catch (NumberFormatException e) {
+                log.error("Error converting content length to number: " + contentLength);
+            }
+        }
+    }
+
+    public void seek(long position) {
+        this.position = position;
+    }
+
+    public long position() {
+        return position;
+    }
+
+
+    @Override
+    public long skip(long n) throws IOException {
+        long bytesToSkip = Math.min(n, contentLength - position);
+        position += bytesToSkip;
+        return bytesToSkip;
+    }
+
+
+    @Override
+    public int read(byte[] buffer, int offset, int len) throws IOException {
+
+        if (log.isDebugEnabled()) {
+            log.debug("read: " + offset + " " + len);
+        }
+        if (offset < 0 || len < 0 || (offset + len) > buffer.length) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        if (len == 0) {
+            return 0;
+        }
+
+
+        HttpURLConnection connection = null;
+        InputStream is = null;
+        String byteRange = "";
+        int n = 0;
+        try {
+            connection = HttpUtils.openConnection(url);
+
+            long endRange = position + len - 1;
+            // IF we know the total content length, limit the end range to that.
+            if (contentLength > 0) {
+                endRange = Math.min(endRange, contentLength);
+            }
+            byteRange = "bytes=" + position + "-" + endRange;
+            connection.setRequestProperty("Range", byteRange);
+            is = connection.getInputStream();
+
+            while (n < len) {
+                int count = is.read(buffer, offset + n, len - n);
+                if (count < 0) {
+                    if (n == 0) {
+                        return -1;
+                    } else {
+                        break;
+                    }
+                }
+                n += count;
+            }
+
+            position += n;
+
+            return n;
+
+        }
+
+        catch (IOException e) {
+            // THis is a bit of a hack, but its not clear how else to handle this.  If a byte range is specified
+            // that goes past the end of the file the response code will be 416.  The MAC os translates this to
+            // an IOException with the 416 code in the message.  Windows translates the error to an EOFException.
+            //
+            //  The BAM file iterator  uses the return value to detect end of file (specifically looks for n == 0).
+            if (e.getMessage().contains("416") || (e instanceof EOFException)) {
+                if (n < 0) {
+                    return -1;
+                } else {
+                    position += n;
+                    // As we are at EOF, the contentLength and position are by definition =
+                    contentLength = position;
+                    return n;
+                }
+            } else {
+                throw e;
+            }
+        }
+
+        finally {
+            if (is != null) {
+                is.close();
+            }
+            if (connection != null) {
+                connection.disconnect();
+            }
+        }
+    }
+
+
+    public void close() throws IOException {
+        BufferedInputStream bis;
+        // Nothing to do
+    }
+
+    public int read() throws IOException {
+        throw new UnsupportedOperationException("read() is not supported on SeekableHTTPStream.  Must read in blocks.");
+    }
+
+    private void logHeaderFields(HttpURLConnection connection) {
+        Map<String, List<String>> map = connection.getHeaderFields();
+
+        for (Map.Entry<String, List<String>> entry : map.entrySet()) {
+            System.out.print(entry.getKey() + ":\t");
+            for (String v : entry.getValue()) {
+                System.out.print(v + " ");
+            }
+            System.out.println();
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/org/broad/tribble/util/SeekableSplitStream.java b/src/org/broad/tribble/util/SeekableSplitStream.java
new file mode 100644
index 0000000..4d6fe63
--- /dev/null
+++ b/src/org/broad/tribble/util/SeekableSplitStream.java
@@ -0,0 +1,143 @@
+package org.broad.tribble.util;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class to wrap
+ */
+public class SeekableSplitStream extends SeekableStream {
+
+    long position = 0;
+    long length = 0;
+    List<PartDescriptor> descriptors;
+    int currentStreamIndex = 0;
+
+    public SeekableSplitStream(String path) throws IOException {
+        parseDescriptors(path);
+    }
+
+    public void seek(long position) throws IOException {
+        this.position = position;
+        long end = 0;
+        long start = 0;
+        for (PartDescriptor desc : descriptors) {
+            end += desc.getContentLength();
+            if (position >= start && position < end) {
+                long delta = position - start;
+                desc.getStream().seek(delta);
+            } else {
+                desc.getStream().seek(0);
+            }
+            start = end;
+        }
+    }
+
+    public long position() throws IOException {
+        return position;
+    }
+
+    @Override
+    public int read(byte[] buffer, int off, int len) throws IOException {
+
+        int bytesRead = 0;
+        long end = 0;
+        long start = 0;
+        for (PartDescriptor desc : descriptors) {
+            end += desc.getContentLength();
+            if (position >= start && position < end) {
+                int delta = (int) (desc.contentLength - desc.stream.position());
+                int l = Math.min(len - bytesRead, delta);
+                int nBytes = desc.stream.read(buffer, off + bytesRead, l);
+                if (nBytes < 0) {
+                    return nBytes;
+                }
+                bytesRead += nBytes;
+                position += nBytes;
+                if (bytesRead >= len) {
+                    break;
+                }
+            }
+            start = end;
+        }
+
+
+        // If we get this far, and haven't read any bytes, we're at EOF
+        return bytesRead > 0 ? bytesRead : -1;
+    }
+
+    @Override
+    public int read() throws IOException {
+        int b = descriptors.get(currentStreamIndex).getStream().read();
+        position++;
+        return b;
+    }
+
+    @Override
+    public void close() throws IOException {
+        for (PartDescriptor desc : descriptors) {
+            desc.getStream().close();
+        }
+    }
+
+    private void parseDescriptors(String path) throws IOException {
+
+        BufferedReader br = null;
+        descriptors = new ArrayList();
+        try {
+            br = ParsingUtils.openBufferedReader(path);
+            String nextLine;
+            while ((nextLine = br.readLine()) != null) {
+                String[] tokens = nextLine.split(" ");
+                if (tokens.length == 2) {
+                    String p = tokens[0];
+
+                    // Require the files are in the same directory as the list file
+                    String listFileName = null;
+                    if(path.startsWith("http:") || path.startsWith("https:")) {
+                       URL url = new URL(path);
+                       listFileName = (new File(url.getPath())).getName();
+                    }
+                    else {
+                        listFileName = (new File(path)).getName();
+                    }
+                    SeekableStream stream = SeekableStreamFactory.getStreamFor(path.replace(listFileName, p));
+
+                    long length = Long.parseLong(tokens[1]);
+                    descriptors.add(new SeekableSplitStream.PartDescriptor(length, stream));
+                } else {
+                    // TODO -- throw exception, or warning?
+                }
+            }
+        }
+        finally {
+            if (br != null) {
+                br.close();
+            }
+        }
+    }
+
+
+
+    public static class PartDescriptor {
+        private long contentLength;
+        private SeekableStream stream;
+
+        public PartDescriptor(long contentLength, SeekableStream stream) {
+            this.contentLength = contentLength;
+            this.stream = stream;
+        }
+
+        public long getContentLength() {
+            return contentLength;
+        }
+
+        public SeekableStream getStream() {
+            return stream;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/org/broad/tribble/util/SeekableStream.java b/src/org/broad/tribble/util/SeekableStream.java
new file mode 100644
index 0000000..3a4596a
--- /dev/null
+++ b/src/org/broad/tribble/util/SeekableStream.java
@@ -0,0 +1,18 @@
+package org.broad.tribble.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: Nov 29, 2009
+ * Time: 10:39:49 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public abstract class SeekableStream extends InputStream {
+
+    public abstract void seek(long position) throws IOException;
+
+    public abstract long position() throws IOException;
+}
\ No newline at end of file
diff --git a/src/org/broad/tribble/util/SeekableStreamFactory.java b/src/org/broad/tribble/util/SeekableStreamFactory.java
new file mode 100644
index 0000000..61c8ce3
--- /dev/null
+++ b/src/org/broad/tribble/util/SeekableStreamFactory.java
@@ -0,0 +1,28 @@
+package org.broad.tribble.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: jrobinso
+ * Date: Nov 30, 2009
+ * Time: 9:05:10 AM
+ * To change this template use File | Settings | File Templates.
+ */
+public class SeekableStreamFactory {
+
+    public static SeekableStream getStreamFor(String path) throws IOException {
+
+        if (path.endsWith(".split")) {
+            return new SeekableSplitStream(path);
+
+        } else {
+            return path.startsWith("http:") ?
+                    new SeekableHTTPStream(new URL(path)) :
+                    new SeekableFileStream(new File(path));
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/org/j3d/io/EndianConverter.java b/src/org/j3d/io/EndianConverter.java
deleted file mode 100644
index 09758b7..0000000
--- a/src/org/j3d/io/EndianConverter.java
+++ /dev/null
@@ -1,1483 +0,0 @@
-/*****************************************************************************
- * LittleEndianConverter.java
- * Java Source
- *
- * This source is licensed under the GNU LGPL v2.1.
- * Please read http://www.gnu.org/copyleft/lgpl.html for more information.
- *
- * This software is not designed or intended for use in on-line control of
- * aircraft, air traffic, aircraft navigation or aircraft communications; or in
- * the design, construction, operation or maintenance of any nuclear
- * facility. Licensee represents and warrants that it will not use or
- * redistribute the Software for such purposes.
- *
- * Copyright (c) 2001, 2002 Dipl. Ing. P. Szawlowski
- * University of Vienna, Dept. of Medical Computer Sciences
- ****************************************************************************/
-
-package org.j3d.io;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * Utility to convert little endian data to big endian data. Includes methods
- * to read from an <code>InputStream</code> and write to an
- * <code>OutputStream</code>.
- * <p/>
- * Todo: extend to convert big endian to little endain data and write to
- * <code>OutputStream</code>
- *
- * @author Dipl. Ing. Paul Szawlowski -
- *         University of Vienna, Dept. of Medical Computer Sciences
- * @version $Revision: 1.1 $
- */
-public class EndianConverter {
-    /**
-     * Converts byte in little/big endian order in <code>srcBuffer</code> to
-     * big/little endian signed short (2 bytes long) data.
-     *
-     * @param srcBuffer  Bytes in little/big endian order which shall be
-     *                   converted. The size of the array must be at least 2.
-     * @param destBuffer Buffer to store the converted data. The size of the
-     *                   array must be at least <code>destOffset</code> +
-     *                   <code>destLength</code>.
-     * @param srcLength  Number of bytes of <code>srcBuffer</code> which shall
-     *                   be processed. Must be <= length of <code>srcBuffer</code>.
-     * @param destOffset Offset for writing converted data in
-     *                   <code>destBuffer</code>.
-     * @param destLength Max. number of data to be written in
-     *                   <code>destBuffer</code>
-     * @return (even) number of processed bytes of srcBuffer
-     */
-    public static int convert
-            (
-                    final byte[] srcBuffer,
-                    final short[] destBuffer,
-                    final int srcLength,
-                    final int destOffset,
-                    final int destLength
-            ) {
-        return convert
-                (
-                        srcBuffer,
-                        destBuffer,
-                        srcLength,
-                        destOffset,
-                        destLength,
-                        (short) 0xff
-                );
-    }
-
-    /**
-     * Converts bytes in little/big endian order in <code>srcBuffer</code> to
-     * big/little endian short (2 bytes long) data. Significant bits can be
-     * masked, e. g. to get unsigned 7 bit values use <code>0x7f</code> as mask.
-     *
-     * @param srcBuffer  Bytes in little/big endian order which shall be
-     *                   converted. The size of the array must be at least 2.
-     * @param destBuffer Buffer to store the converted data. The size of the
-     *                   array must be at least <code>destOffset</code> +
-     *                   <code>destLength</code>.
-     * @param srcLength  Number of bytes of <code>srcBuffer</code> which shall
-     *                   be processed. Must be <= length of <code>srcBuffer</code>.
-     * @param destOffset Offset for writing converted data in
-     *                   <code>destBuffer</code>.
-     * @param destLength Max. number of data to be written in
-     *                   <code>destBuffer</code>
-     * @param mask       Mask for significant bits. Set significant bits to 1.
-     * @return (even) number of processed bytes of srcBuffer
-     */
-    public static int convert
-            (
-                    final byte[] srcBuffer,
-                    final short[] destBuffer,
-                    int srcLength,
-                    final int destOffset,
-                    final int destLength,
-                    final short mask
-            ) {
-        srcLength = Math.min(destLength * 2, (srcLength / 2) * 2);
-        for (int i = 0; i < srcLength; i += 2) {
-            final int tmp =
-                    (srcBuffer[i] & 0xff | (srcBuffer[i + 1] << 8)) & mask;
-            destBuffer[(i / 2) + destOffset] = (short) tmp;
-        }
-        return srcLength;
-    }
-
-    /**
-     * Converts bytes in little/big endian order in <code>srcBuffer</code> to
-     * big/little endian signed integer (4 bytes long) data.
-     *
-     * @param srcBuffer  Bytes in little/big endian order which shall be
-     *                   converted. The size of the array must be at least 4.
-     * @param destBuffer Buffer to store the converted data. The size of the
-     *                   array must be at least <code>destOffset</code> +
-     *                   <code>destLength</code>.
-     * @param srcLength  Number of bytes of <code>srcBuffer</code> which shall
-     *                   be processed. Must be <= length of <code>srcBuffer</code>.
-     * @param destOffset Offset for writing converted data in
-     *                   <code>destBuffer</code>.
-     * @param destLength Maximum number of data to be written in
-     *                   <code>destBuffer</code>
-     * @return number of processed bytes of srcBuffer (multiple of 4 )
-     */
-    public static int convert
-            (
-                    final byte[] srcBuffer,
-                    final int[] destBuffer,
-                    final int srcLength,
-                    final int destOffset,
-                    final int destLength
-            ) {
-        return convert
-                (
-                        srcBuffer,
-                        destBuffer,
-                        srcLength,
-                        destOffset,
-                        destLength,
-                        0xffffffff
-                );
-    }
-
-    /**
-     * Converts bytes in little/big endian order in <code>srcBuffer</code> to
-     * big/little endian integer (4 bytes long) data. Significant bits can be
-     * masked, e. g. to get unsigned 31 bit values use <code>0x7fffffff</code>
-     * as mask.
-     *
-     * @param srcBuffer  Bytes in little/big endian order which shall be
-     *                   converted. The size of the array must be at least 4.
-     * @param destBuffer Buffer to store the converted data. The size of the
-     *                   array must be at least <code>destOffset</code> +
-     *                   <code>destLength</code>.
-     * @param srcLength  Number of bytes of <code>srcBuffer</code> which shall
-     *                   be processed. Must be <= length of <code>srcBuffer</code>.
-     * @param destOffset Offset for writing converted data in
-     *                   <code>destBuffer</code>.
-     * @param destLength Maximum number of data to be written in
-     *                   <code>destBuffer</code>
-     * @param mask       Mask for significant bits. Set significant bits to 1.
-     * @return number of processed bytes of srcBuffer (multiple of 4 )
-     */
-    public static int convert
-            (
-                    final byte[] srcBuffer,
-                    final int[] destBuffer,
-                    int srcLength,
-                    final int destOffset,
-                    final int destLength,
-                    final int mask
-            ) {
-        srcLength = Math.min(destLength * 4, (srcLength / 4) * 4);
-        for (int i = 0; i < srcLength; i += 4) {
-            destBuffer[(i / 4) + destOffset] = (srcBuffer[i] & 0xff
-                    | (srcBuffer[i + 1] << 8) & 0xff00
-                    | (srcBuffer[i + 2] << 16) & 0xff0000
-                    | (srcBuffer[i + 3] << 24)) & mask;
-        }
-        return srcLength;
-    }
-
-    /**
-     * Converts bytes in little/big endian order in <code>srcBuffer</code> to
-     * big/little endian long (8 bytes long) data.
-     *
-     * @param srcBuffer  Bytes in little/big endian order which shall be
-     *                   converted. The size of the array must be at least 8.
-     * @param destBuffer Buffer to store the converted data. The size of the
-     *                   array must be at least <code>destOffset</code> +
-     *                   <code>destLength</code>.
-     * @param srcLength  Number of bytes of <code>srcBuffer</code> which shall
-     *                   be processed. Must be <= length of <code>srcBuffer</code>.
-     * @param destOffset Offset for writing converted data in
-     *                   <code>destBuffer</code>.
-     * @param destLength Maximum number of data to be written in
-     *                   <code>destBuffer</code>
-     * @return number of processed bytes of srcBuffer (multiple of 8 )
-     */
-    public static int convert
-            (
-                    final byte[] srcBuffer,
-                    final long[] destBuffer,
-                    final int srcLength,
-                    final int destOffset,
-                    final int destLength
-            ) {
-        return convert
-                (
-                        srcBuffer,
-                        destBuffer,
-                        srcLength,
-                        destOffset,
-                        destLength,
-                        0xffffffffffffffffL
-                );
-
-    }
-
-    /**
-     * Converts bytes in little/big endian order in <code>srcBuffer</code> to
-     * big/little endian long (8 bytes long) data. Significant bits can be
-     * masked, e. g. to get unsigned 63 bit values use
-     * <code>0x7fffffffffffffff</code> as mask.
-     *
-     * @param srcBuffer  Bytes in little/big endian order which shall be
-     *                   converted. The size of the array must be at least 8.
-     * @param destBuffer Buffer to store the converted data. The size of the
-     *                   array must be at least <code>destOffset</code> +
-     *                   <code>destLength</code>.
-     * @param srcLength  Number of bytes of <code>srcBuffer</code> which shall
-     *                   be processed. Must be <= length of <code>srcBuffer</code>.
-     * @param destOffset Offset for writing converted data in
-     *                   <code>destBuffer</code>.
-     * @param destLength Maximum number of data to be written in
-     *                   <code>destBuffer</code>
-     * @param mask       Mask for significant bits. Set significant bits to 1.
-     * @return number of processed bytes of srcBuffer (multiple of 8 )
-     */
-    public static int convert
-            (
-                    final byte[] srcBuffer,
-                    final long[] destBuffer,
-                    int srcLength,
-                    final int destOffset,
-                    final int destLength,
-                    final long mask
-            ) {
-        srcLength = Math.min(destLength * 8, (srcLength / 8) * 8);
-        for (int i = 0; i < srcLength; i += 8) {
-            destBuffer[(i / 8) + destOffset] = (srcBuffer[i] & 0xff
-                    | (srcBuffer[i + 1] << 8) & 0xff00
-                    | (srcBuffer[i + 2] << 16) & 0xff0000
-                    | (srcBuffer[i + 3] << 24) & 0xff000000
-                    | (srcBuffer[i + 3] << 32) & 0xff00000000L
-                    | (srcBuffer[i + 5] << 40) & 0xff0000000000L
-                    | (srcBuffer[i + 6] << 48) & 0xff000000000000L
-                    | (srcBuffer[i + 7] << 56)) & mask;
-        }
-        return srcLength;
-    }
-
-    /**
-     * Convert a little/big endian signed short to a big/little endian signed
-     * short
-     *
-     * @param value number to convert
-     */
-    public static short convert(final short value) {
-        return (short) ((value >> 8) & 0xff | (value << 8));
-    }
-
-    /**
-     * Convert a little/big endian signed integer to a big/little endian signed
-     * integer
-     *
-     * @param value number to convert
-     */
-    public static int convert(final int value) {
-        return (value >> 24) & 0xff
-                | (value >> 8) & 0xff00
-                | (value << 8) & 0xff0000
-                | value << 24;
-    }
-
-    /**
-     * Convert a little/big endian signed long to a big/little endian signed
-     * long
-     *
-     * @param value number to convert
-     */
-    public static long convert(final long value) {
-        return (value >> 56) & 0xff
-                | (value >> 40) & 0xff00
-                | (value >> 24) & 0xff0000
-                | (value >> 8) & 0xff000000
-                | (value << 8) & 0xff00000000L
-                | (value << 24) & 0xff00000000L
-                | (value << 56) & 0xff0000000000L;
-    }
-
-    /**
-     * Converts bytes in <code>srcBuffer</code> in little endian order to float
-     * data.
-     *
-     * @param srcBuffer  Bytes in little endian order which shall be
-     *                   converted. The size of the array must be at least 4.
-     * @param destBuffer Buffer to store the converted data. The size of the
-     *                   array must be at least <code>destOffset</code> +
-     *                   <code>destLength</code>.
-     * @param srcLength  Number of bytes of <code>srcBuffer</code> which shall
-     *                   be processed. Must be <= length of <code>srcBuffer</code>.
-     * @param destOffset Offset for writing converted data in
-     *                   <code>destBuffer</code>.
-     * @param destLength Maximum number of data to be written in
-     *                   <code>destBuffer</code>
-     * @return number of processed bytes of srcBuffer (multiple of 4 )
-     */
-    public static int convertLittleEndianToFloat
-            (
-                    final byte[] srcBuffer,
-                    final float[] destBuffer,
-                    int srcLength,
-                    final int destOffset,
-                    final int destLength
-            ) {
-        srcLength = Math.min(destLength * 4, (srcLength / 4) * 4);
-        for (int i = 0; i < srcLength; i += 4) {
-            destBuffer[(i / 4) + destOffset] = Float.intBitsToFloat
-                    (
-                            srcBuffer[i] & 0xff
-                                    | (srcBuffer[i + 1] << 8) & 0xff00
-                                    | (srcBuffer[i + 2] << 16) & 0xff0000
-                                    | (srcBuffer[i + 3] << 24)
-                    );
-        }
-        return srcLength;
-    }
-
-    /**
-     * Converts bytes in <code>srcBuffer</code> in little endian order to double
-     * data.
-     *
-     * @param srcBuffer  Bytes in little endian order which shall be
-     *                   converted. The size of the array must be at least 8.
-     * @param destBuffer Buffer to store the converted data. The size of the
-     *                   array must be at least <code>destOffset</code> +
-     *                   <code>destLength</code>.
-     * @param srcLength  Number of bytes of <code>srcBuffer</code> which shall
-     *                   be processed. Must be <= length of <code>srcBuffer</code>.
-     * @param destOffset Offset for writing converted data in
-     *                   <code>destBuffer</code>.
-     * @param destLength Maximum number of data to be written in
-     *                   <code>destBuffer</code>
-     * @return number of processed bytes of srcBuffer (multiple of 8 )
-     */
-    public static int convertLittleEndianToDouble
-            (
-                    final byte[] srcBuffer,
-                    final double[] destBuffer,
-                    int srcLength,
-                    final int destOffset,
-                    final int destLength
-            ) {
-        srcLength = Math.min(destLength * 8, (srcLength / 8) * 8);
-        for (int i = 0; i < srcLength; i += 8) {
-            destBuffer[(i / 8) + destOffset] = Double.longBitsToDouble
-                    (
-                            srcBuffer[i] & 0xff
-                                    | (srcBuffer[i + 1] << 8) & 0xff00
-                                    | (srcBuffer[i + 2] << 16) & 0xff0000
-                                    | (srcBuffer[i + 3] << 24) & 0xff000000
-                                    | (srcBuffer[i + 3] << 32) & 0xff00000000L
-                                    | (srcBuffer[i + 5] << 40) & 0xff0000000000L
-                                    | (srcBuffer[i + 6] << 48) & 0xff000000000000L
-                                    | (srcBuffer[i + 7] << 56)
-                    );
-        }
-        return srcLength;
-    }
-
-    /**
-     * Converts bytes in little/big endian order in <code>srcBuffer</code> to
-     * big/little endian signed integer data with a user defined block size of
-     * 2, 3, or 4 bytes. <p>
-     *
-     * @param srcBuffer  Bytes in little/big endian order which shall be
-     *                   converted. The size of the array must be at least
-     *                   <code>blockSize</code>.
-     * @param destBuffer Buffer to store the converted data. The size of the
-     *                   array must be at least <code>destOffset</code> +
-     *                   <code>destLength</code>.
-     * @param srcLength  Number of bytes of <code>srcBuffer</code> which shall
-     *                   be processed. Must be <= length of <code>srcBuffer</code>.
-     * @param destOffset Offset for writing converted data in
-     *                   <code>destBuffer</code>.
-     * @param destLength Maximum number of data to be written in
-     *                   <code>destBuffer</code>
-     * @param blockSize  May be 2, 3 or 4.
-     * @return number of processed bytes of srcBuffer (multiple of
-     *         <code>blockSize</code>)
-     */
-    public static int convert
-            (
-                    final int blockSize,
-                    final byte[] srcBuffer,
-                    final int[] destBuffer,
-                    final int srcLength,
-                    final int destOffset,
-                    final int destLength
-            ) {
-        return convert
-                (
-                        blockSize,
-                        srcBuffer,
-                        destBuffer,
-                        srcLength,
-                        destOffset,
-                        destLength,
-                        0xffffffff
-                );
-    }
-
-    /**
-     * Converts bytes in little/big endian order in <code>srcBuffer</code> to
-     * big/little endian signed integer data with a user defined block size of
-     * 2, 3, or 4 bytes. Significant bits can be masked, e. g. to get unsigned
-     * 16 bit values use <code>0xffff</code> as mask.<p>
-     *
-     * @param blockSize  May be 2, 3 or 4.
-     * @param srcBuffer  Bytes in little/big endian order which shall be
-     *                   converted. The size of the array must be at least
-     *                   <code>blockSize</code>.
-     * @param destBuffer Buffer to store the converted data. The size of the
-     *                   array must be at least <code>destOffset</code> +
-     *                   <code>destLength</code>.
-     * @param srcLength  Number of bytes of <code>srcBuffer</code> which shall
-     *                   be processed. Must be <= length of <code>srcBuffer</code>.
-     * @param destOffset Offset for writing converted data in
-     *                   <code>destBuffer</code>.
-     * @param destLength Maximum number of data to be written in
-     *                   <code>destBuffer</code>
-     * @param mask       Mask for significant bits. Set significant bits to 1.
-     * @return number of processed bytes of srcBuffer (multiple of
-     *         <code>blockSize</code>)
-     */
-    public static int convert
-            (
-                    final int blockSize,
-                    final byte[] srcBuffer,
-                    final int[] destBuffer,
-                    final int srcLength,
-                    final int destOffset,
-                    final int destLength,
-                    final int mask
-            ) {
-        final int length = Math.min
-                (
-                        destLength * blockSize,
-                        (srcLength / blockSize) * blockSize
-                );
-        if (blockSize == 2) {
-            for (int i = 0; i < length; i += 2) {
-                destBuffer[(i / 2) + destOffset] =
-                        (srcBuffer[i] & 0xff | (srcBuffer[i + 1] << 8))
-                                & mask;
-            }
-            return length;
-        } else if (blockSize == 3) {
-            for (int i = 0; i < length; i += 3) {
-                destBuffer[(i / 3) + destOffset] = (srcBuffer[i] & 0xff
-                        | (srcBuffer[i + 1] << 8) & 0xff00
-                        | (srcBuffer[i + 2] << 24)) & mask;
-            }
-            return length;
-        } else if (blockSize == 4) {
-            return convert
-                    (
-                            srcBuffer,
-                            destBuffer,
-                            srcLength,
-                            destOffset,
-                            destLength,
-                            mask
-                    );
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Converts signed short data in <code>srcBuffer</code> to bytes in little
-     * endian order.
-     *
-     * @param srcBuffer  Signed short data to be converted. The size of array
-     *                   must be at least <code>srcOffset</code> + <code>srcLength</code>.
-     * @param destBuffer Buffer to store the converted data bytes in little
-     *                   endian order. The first valid byte will start at 0. The size of
-     *                   the array must be at least 2.
-     * @param srcOffset  Offset for reading data from <code>srcBuffer</code>.
-     * @param srcLength  Max. Number of data to be processed from
-     *                   <code>srcBuffer</code>.
-     * @return number of processed short data from <code>srcBuffer</code>.
-     *         Multiply by 2 to get the number of valid bytes in
-     *         <code>destBuffer</code>.
-     */
-    public static int convertToLittleEndian
-            (
-                    final short[] srcBuffer,
-                    final byte[] destBuffer,
-                    final int srcOffset,
-                    int srcLength
-            ) {
-        srcLength = Math.min(srcLength, destBuffer.length / 2);
-        int index = 0;
-        for (int i = 0; i < srcLength; i++) {
-            final short data = srcBuffer[srcOffset + i];
-            destBuffer[index++] = (byte) (data);
-            destBuffer[index++] = (byte) (data >> 8);
-        }
-        return srcLength;
-    }
-
-    /**
-     * Converts signed int data in <code>srcBuffer</code> to bytes in little
-     * endian order.
-     *
-     * @param srcBuffer  Signed int data to be converted. The size of array
-     *                   must be at least <code>srcOffset</code> + <code>srcLength</code>.
-     * @param destBuffer Buffer to store the converted data bytes in little
-     *                   endian order. The first valid byte will start at 0. The size of
-     *                   the array must be at least 4.
-     * @param srcOffset  Offset for reading data from <code>srcBuffer</code>.
-     * @param srcLength  Max. Number of data to be processed from
-     *                   <code>srcBuffer</code>.
-     * @return number of processed short data from <code>srcBuffer</code>.
-     *         Multiply by 4 to get the number of valid bytes in
-     *         <code>destBuffer</code>.
-     */
-    public static int convertToLittleEndian
-            (
-                    final int[] srcBuffer,
-                    final byte[] destBuffer,
-                    final int srcOffset,
-                    int srcLength
-            ) {
-        srcLength = Math.min(srcLength, destBuffer.length / 4);
-        int index = 0;
-        for (int i = 0; i < srcLength; i++) {
-            final int data = srcBuffer[srcOffset + i];
-            destBuffer[index++] = (byte) (data);
-            destBuffer[index++] = (byte) (data >> 8);
-            destBuffer[index++] = (byte) (data >> 16);
-            destBuffer[index++] = (byte) (data >> 24);
-        }
-        return srcLength;
-    }
-
-    /**
-     * Converts signed int data in <code>srcBuffer</code> to bytes in little
-     * endian order with a user defined block size of 1, 2, 3 or 4 (1 is here
-     * for convinience).
-     *
-     * @param blockSize  May be 1, 2, 3 or 4.
-     * @param srcBuffer  Signed int data to be converted. The size of array
-     *                   must be at least <code>srcOffset</code> + <code>srcLength</code>.
-     * @param destBuffer Buffer to store the converted data bytes in little
-     *                   endian order. The first valid byte will start at 0. The size of
-     *                   the array must be at least <code>blockSize</code>.
-     * @param srcOffset  Offset for reading data from <code>srcBuffer</code>.
-     * @param srcLength  Max. Number of data to be processed from
-     *                   <code>srcBuffer</code>.
-     * @return number of processed short data from <code>srcBuffer</code>.
-     *         Multiply by <code>blockSize</code> to get the number of valid bytes
-     *         in  <code>destBuffer</code>.
-     */
-    public static int convertToLittleEndian
-            (
-                    final int blockSize,
-                    final int[] srcBuffer,
-                    final byte[] destBuffer,
-                    final int srcOffset,
-                    int srcLength
-            ) {
-        srcLength = Math.min(srcLength, destBuffer.length / blockSize);
-        int index = 0;
-        for (int i = 0; i < srcLength; i++) {
-            final int data = srcBuffer[srcOffset + i];
-            destBuffer[index++] = (byte) (data);
-            if (blockSize >= 2) {
-                destBuffer[index++] = (byte) (data >> 8);
-                if (blockSize >= 3) {
-                    destBuffer[index++] = (byte) (data >> 16);
-                    if (blockSize == 4) {
-                        destBuffer[index++] = (byte) (data >> 24);
-                    }
-                }
-            }
-        }
-        return srcLength;
-    }
-
-    /**
-     * Converts signed long data in <code>srcBuffer</code> to bytes in little
-     * endian order.
-     *
-     * @param srcBuffer  Signed long data to be converted. The size of array
-     *                   must be at least <code>srcOffset</code> + <code>srcLength</code>.
-     * @param destBuffer Buffer to store the converted data bytes in little
-     *                   endian order. The first valid byte will start at 0. The size of
-     *                   the array must be at least 8.
-     * @param srcOffset  Offset for reading data from <code>srcBuffer</code>.
-     * @param srcLength  Max. Number of data to be processed from
-     *                   <code>srcBuffer</code>.
-     * @return number of processed short data from <code>srcBuffer</code>.
-     *         Multiply by 8 to get the number of valid bytes in
-     *         <code>destBuffer</code>.
-     */
-    public static int convertToLittleEndian
-            (
-                    final long[] srcBuffer,
-                    final byte[] destBuffer,
-                    final int srcOffset,
-                    int srcLength
-            ) {
-        srcLength = Math.min(srcLength, destBuffer.length / 8);
-        int index = 0;
-        for (int i = 0; i < srcLength; i++) {
-            long data = srcBuffer[srcOffset + i];
-            for (int j = 0; j < 8; j++) {
-                destBuffer[index++] = (byte) (data);
-                data >>= 8;
-            }
-        }
-        return srcLength;
-    }
-
-    /**
-     * Converts float data in <code>srcBuffer</code> to bytes in little
-     * endian order.For the conversion the <code>Float.floatToIntBits</code>
-     * method is used.
-     *
-     * @param srcBuffer  Float data to be converted. The size of array
-     *                   must be at least <code>srcOffset</code> + <code>srcLength</code>.
-     * @param destBuffer Buffer to store the converted data bytes in little
-     *                   endian order. The first valid byte will start at 0. The size of
-     *                   the array must be at least 4.
-     * @param srcOffset  Offset for reading data from <code>srcBuffer</code>.
-     * @param srcLength  Max. Number of data to be processed from
-     *                   <code>srcBuffer</code>.
-     * @return number of processed short data from <code>srcBuffer</code>.
-     *         Multiply by 4 to get the number of valid bytes in
-     *         <code>destBuffer</code>.
-     */
-    public static int convertToLittleEndian
-            (
-                    final float[] srcBuffer,
-                    final byte[] destBuffer,
-                    final int srcOffset,
-                    int srcLength
-            ) {
-        srcLength = Math.min(srcLength, destBuffer.length / 4);
-        int index = 0;
-        for (int i = 0; i < srcLength; i++) {
-            final int data =
-                    Float.floatToIntBits(srcBuffer[srcOffset + i]);
-            destBuffer[index++] = (byte) (data);
-            destBuffer[index++] = (byte) (data >> 8);
-            destBuffer[index++] = (byte) (data >> 16);
-            destBuffer[index++] = (byte) (data >> 24);
-        }
-        return srcLength;
-    }
-
-    /**
-     * Converts double data in <code>srcBuffer</code> to bytes in little
-     * endian order. For the conversion the <code>Double.doubleToLongBits</code>
-     * method is used.
-     *
-     * @param srcBuffer  Double data to be converted. The size of array
-     *                   must be at least <code>srcOffset</code> + <code>srcLength</code>.
-     * @param destBuffer Buffer to store the converted data bytes in little
-     *                   endian order. The first valid byte will start at 0. The size of
-     *                   the array must be at least 8.
-     * @param srcOffset  Offset for reading data from <code>srcBuffer</code>.
-     * @param srcLength  Max. Number of data to be processed from
-     *                   <code>srcBuffer</code>.
-     * @return number of processed short data from <code>srcBuffer</code>.
-     *         Multiply by 8 to get the number of valid bytes in
-     *         <code>destBuffer</code>.
-     */
-    public static int convertToLittleEndian
-            (
-                    final double[] srcBuffer,
-                    final byte[] destBuffer,
-                    final int srcOffset,
-                    int srcLength
-            ) {
-        srcLength = Math.min(srcLength, destBuffer.length / 8);
-        int index = 0;
-        for (int i = 0; i < srcLength; i++) {
-            long data = Double.doubleToLongBits(srcBuffer[srcOffset + i]);
-            for (int j = 0; j < 8; j++) {
-                destBuffer[index++] = (byte) (data);
-                data >>= 8;
-            }
-        }
-        return srcLength;
-    }
-
-
-    /**
-     * Reads little/big endian data from an <code>InputStream</code> and
-     * converts it to big/little endian signed short (2 bytes long) data.
-     *
-     * @param readBuffer Auxilary Buffer to be used to read from
-     *                   <code>stream</code>. Choose an appropriate size (multiple of 2)
-     *                   depending on the size of the stream. The size of the array must be
-     *                   at least 2.
-     * @param destBuffer Buffer to store the converted data. The size of the
-     *                   array must be at least <code>destOffset</code> +
-     *                   <code>destLength</code>.
-     * @param destOffset Offset for writing converted data in
-     *                   <code>destBuffer</code>.
-     * @param destLength Max. number of data to be written in
-     *                   <code>destBuffer</code>
-     * @param stream     <code>InputStream</code> to read from.
-     * @return number of data elements written in <code>destBuffer</code>
-     *         (will be <= destLength).
-     */
-    public static int read
-            (
-                    final byte[] readBuffer,
-                    final short[] destBuffer,
-                    final int destOffset,
-                    final int destLength,
-                    final InputStream stream
-            )
-            throws IOException {
-        return read
-                (
-                        readBuffer,
-                        destBuffer,
-                        destOffset,
-                        destLength,
-                        stream,
-                        (short) 0xff
-                );
-    }
-
-    /**
-     * Reads little/big endian data from an <code>InputStream</code> and
-     * converts it to big/little endian short (2 bytes long) data.
-     * Significant bits can be masked, e. g. to get unsigned 7 bit values use
-     * <code>0x7f</code> as mask.
-     *
-     * @param readBuffer Auxilary Buffer to be used to read from
-     *                   <code>stream</code>. Choose an appropriate size (multiple of 2)
-     *                   depending on the size of the stream. The size of the array must be
-     *                   at least 2.
-     * @param destBuffer Buffer to store the converted data. The size of the
-     *                   array must be at least <code>destOffset</code> +
-     *                   <code>destLength</code>.
-     * @param destOffset Offset for writing converted data in
-     *                   <code>destBuffer</code>.
-     * @param destLength Max. number of data to be written in
-     *                   <code>destBuffer</code>
-     * @param stream     <code>InputStream</code> to read from.
-     * @param mask       Mask for significant bits. Set significant bits to 1.
-     * @return number of data elements written in <code>destBuffer</code>
-     *         (will be <= destLength).
-     */
-    public static int read
-            (
-                    final byte[] readBuffer,
-                    final short[] destBuffer,
-                    final int destOffset,
-                    final int destLength,
-                    final InputStream stream,
-                    final short mask
-            )
-            throws IOException {
-        int numOfBytesRead = 0;
-        int numOfData = 0;
-        int offset = 0;
-        final int length = (readBuffer.length / 2) * 2;
-        while ((numOfBytesRead >= 0) && (numOfData < destLength)) {
-            // calculate how many more bytes can be read so that destBuffer
-            // does not overflow; enables to continue reading from same stream
-            // without data loss
-            final int maxBytesToRead =
-                    Math.min((destLength - numOfData) * 2, length);
-            numOfBytesRead =
-                    stream.read(readBuffer, offset, maxBytesToRead - offset);
-            int numOfProcessedBytes = convert
-                    (
-                            readBuffer,
-                            destBuffer,
-                            numOfBytesRead + offset,
-                            destOffset + numOfData,
-                            destLength - numOfData,
-                            mask
-                    );
-            // if an uneven number of bytes was read from stream
-            if (numOfBytesRead - numOfProcessedBytes == 1) {
-                offset = 1;
-                readBuffer[0] = readBuffer[numOfProcessedBytes];
-            } else {
-                offset = 0;
-            }
-            numOfData += (numOfProcessedBytes / 2);
-        }
-        return numOfData;
-    }
-
-    /**
-     * Reads little/big endian data from an <code>InputStream</code> and
-     * converts it to big/little endian signed int (4 bytes long) data.
-     *
-     * @param readBuffer Auxilary Buffer to be used to read from
-     *                   <code>stream</code>. Choose an appropriate size (multiple of 4)
-     *                   depending on the size of the stream. The size of the array must be
-     *                   at least 4.
-     * @param destBuffer Buffer to store the converted data. The size of the
-     *                   array must be at least <code>destOffset</code> +
-     *                   <code>destLength</code>.
-     * @param destOffset Offset for writing converted data in
-     *                   <code>destBuffer</code>.
-     * @param destLength Max. number of data to be written in
-     *                   <code>destBuffer</code>
-     * @param stream     <code>InputStream</code> to read from.
-     * @return number of data elements written in <code>destBuffer</code>
-     *         (will be <= destLength).
-     */
-    public static int read
-            (
-                    final byte[] readBuffer,
-                    final int[] destBuffer,
-                    final int destOffset,
-                    final int destLength,
-                    final InputStream stream
-            )
-            throws IOException {
-        return read
-                (
-                        readBuffer,
-                        destBuffer,
-                        destOffset,
-                        destLength,
-                        stream,
-                        0xffffffff
-                );
-    }
-
-    /**
-     * Reads little/big endian data from an <code>InputStream</code> and
-     * converts it to big/little endian int (4 bytes long) data. Significant
-     * bits can be masked, e. g. to get unsigned 31 bit values use
-     * <code>0x7fffffff</code> as mask.
-     *
-     * @param readBuffer Auxilary Buffer to be used to read from
-     *                   <code>stream</code>. Choose an appropriate size (multiple of 4)
-     *                   depending on the size of the stream. The size of the array must be
-     *                   at least 4.
-     * @param destBuffer Buffer to store the converted data. The size of the
-     *                   array must be at least <code>destOffset</code> +
-     *                   <code>destLength</code>.
-     * @param destOffset Offset for writing converted data in
-     *                   <code>destBuffer</code>.
-     * @param destLength Max. number of data to be written in
-     *                   <code>destBuffer</code>
-     * @param stream     <code>InputStream</code> to read from.
-     * @param mask       Mask for significant bits. Set significant bits to 1.
-     * @return number of data elements written in <code>destBuffer</code>
-     *         (will be <= destLength).
-     */
-    public static int read
-            (
-                    final byte[] readBuffer,
-                    final int[] destBuffer,
-                    final int destOffset,
-                    final int destLength,
-                    final InputStream stream,
-                    final int mask
-            )
-            throws IOException {
-        int numOfBytesRead = 0;
-        int numOfData = 0;
-        int offset = 0;
-        final int length = (readBuffer.length / 4) * 4;
-        while ((numOfBytesRead >= 0) && (numOfData < destLength)) {
-            // calculate how many more bytes can be read so that destBuffer
-            // does not overflow; enables to continue reading from same stream
-            // without data loss
-            final int maxBytesToRead =
-                    Math.min((destLength - numOfData) * 4, length);
-            numOfBytesRead =
-                    stream.read(readBuffer, offset, maxBytesToRead - offset);
-            int numOfProcessedBytes = convert
-                    (
-                            readBuffer,
-                            destBuffer,
-                            numOfBytesRead + offset,
-                            destOffset + numOfData,
-                            destLength - numOfData,
-                            mask
-                    );
-            offset = numOfBytesRead - numOfProcessedBytes;
-            // if a number of bytes was read from stream was not a multiple
-            // of 4
-            if (offset >= 1) {
-                readBuffer[0] = readBuffer[numOfProcessedBytes];
-                if (offset >= 2) {
-                    readBuffer[1] = readBuffer[numOfProcessedBytes + 1];
-                    if (offset >= 3) {
-                        readBuffer[2] = readBuffer[numOfProcessedBytes + 2];
-                    }
-                }
-            }
-            numOfData += (numOfProcessedBytes / 4);
-        }
-        return numOfData;
-    }
-
-    /**
-     * Reads little/big endian data from an <code>InputStream</code> and
-     * converts it to to big/little endian signed integer data with a user
-     * defined block size of 1, 2, 3, or 4 bytes (1 is here for conveniance).<p>
-     *
-     * @param blockSize  May be 1, 2, 3 or 4.
-     * @param readBuffer Auxilary Buffer to be used to read from
-     *                   <code>stream</code>. Choose an appropriate size (multiple of
-     *                   <code>blockSize</code>) depending on the size of the stream. The
-     *                   size of the array must be at least <code>blockSize</code>.
-     * @param destBuffer Buffer to store the converted data. The size of the
-     *                   array must be at least <code>destOffset</code> +
-     *                   <code>destLength</code>.
-     * @param destOffset Offset for writing converted data in
-     *                   <code>destBuffer</code>.
-     * @param destLength Max. number of data to be written in
-     *                   <code>destBuffer</code>
-     * @param stream     <code>InputStream</code> to read from.
-     * @return number of data elements written in <code>destBuffer</code>
-     *         (will be <= destLength).
-     */
-    public static int read
-            (
-                    final int blockSize,
-                    final byte[] readBuffer,
-                    final int[] destBuffer,
-                    final int destOffset,
-                    final int destLength,
-                    final InputStream stream
-            )
-            throws IOException {
-        return read
-                (
-                        blockSize,
-                        readBuffer,
-                        destBuffer,
-                        destOffset,
-                        destLength,
-                        stream,
-                        0xffffffff
-                );
-    }
-
-    /**
-     * Reads little/big endian data from an <code>InputStream</code> and
-     * converts it to to big/little endian signed integer data with a user
-     * defined block size of 1, 2, 3, or 4 bytes (1 is here for conveniance).
-     * Significant bits can be masked, e. g. to get unsigned 16 bit values use
-     * <code>0xffff</code> as mask.<p>
-     *
-     * @param blockSize  May be 1, 2, 3 or 4.
-     * @param readBuffer Auxilary Buffer to be used to read from
-     *                   <code>stream</code>. Choose an appropriate size (multiple of
-     *                   <code>blockSize</code>) depending on the size of the stream. The
-     *                   size of the array must be at least <code>blockSize</code>.
-     * @param destBuffer Buffer to store the converted data. The size of the
-     *                   array must be at least <code>destOffset</code> +
-     *                   <code>destLength</code>.
-     * @param destOffset Offset for writing converted data in
-     *                   <code>destBuffer</code>.
-     * @param destLength Max. number of data to be written in
-     *                   <code>destBuffer</code>
-     * @param stream     <code>InputStream</code> to read from.
-     * @param mask       Mask for significant bits. Set significant bits to 1.
-     * @return number of data elements written in <code>destBuffer</code>
-     *         (will be <= destLength).
-     */
-    public static int read
-            (
-                    final int blockSize,
-                    final byte[] readBuffer,
-                    final int[] destBuffer,
-                    final int destOffset,
-                    final int destLength,
-                    final InputStream stream,
-                    final int mask
-            )
-            throws IOException {
-        if (blockSize == 2) {
-            return read2ByteBlock
-                    (
-                            readBuffer,
-                            destBuffer,
-                            destOffset,
-                            destLength,
-                            stream,
-                            mask
-                    );
-        } else if (blockSize == 3) {
-            return read3ByteBlock
-                    (
-                            readBuffer,
-                            destBuffer,
-                            destOffset,
-                            destLength,
-                            stream,
-                            mask
-                    );
-        } else if (blockSize == 4) {
-            return read
-                    (
-                            readBuffer,
-                            destBuffer,
-                            destOffset,
-                            destLength,
-                            stream,
-                            mask
-                    );
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Reads 4 bytes in little/big endian format and converts it to a big/little
-     * endian signed int.<p>
-     *
-     * @throws IOException if EOF occurs and only one, 2 or 3 bytes were read or
-     *                     if error during reading occurs
-     */
-    public static int read4ByteBlock(final InputStream stream)
-            throws IOException {
-        return read(stream) & 0xff
-                | (read(stream) << 8) & 0xff00
-                | (read(stream) << 16) & 0xff0000
-                | (read(stream) << 24);
-    }
-
-    /**
-     * Reads 2 bytes in little/big endian format and converts it to a big/little
-     * endian signed int.<p>
-     * To Convert it to an unsigned int <code>&</code> the result with
-     * <code>0xffff</code>.
-     *
-     * @throws IOException if EOF occurs and only one bytes was read or
-     *                     if error during reading occurs
-     */
-    public static int read2ByteBlock(final InputStream stream)
-            throws IOException {
-        return read(stream) & 0xff
-                | (read(stream) << 8);
-    }
-
-    /**
-     * Reads 3 bytes in little/big endian format and converts it to a big/little
-     * endian signed int.<p>
-     * To Convert it to an unsigned int <code>&</code> the result with
-     * <code>0xffffff</code>.
-     *
-     * @throws IOException if EOF occurs and only one or 2 bytes were read or
-     *                     if error during reading occurs
-     */
-    public static int read3ByteBlock(final InputStream stream)
-            throws IOException {
-        return read(stream) & 0xff
-                | (read(stream) << 8) & 0xff00
-                | (read(stream) << 16);
-    }
-
-    /**
-     * Reads 8 bytes in little/big endian format and converts it to a big/little
-     * endian signed long.<p>
-     * To Convert it to an unsigned int <code>&</code> the result with
-     * <code>0xffffff</code>.
-     *
-     * @throws IOException if EOF occurs and only one or 2 bytes were read or
-     *                     if error during reading occurs
-     */
-    public static long read8ByteBlock(final InputStream stream)
-            throws IOException {
-        return read(stream) & 0xff
-                | (read(stream) << 8) & 0xff00
-                | (read(stream) << 16) & 0xff0000
-                | (read(stream) << 24) & 0xff000000
-                | (read(stream) << 32) & 0xff00000000L
-                | (read(stream) << 40) & 0xff0000000000L
-                | (read(stream) << 48) & 0xff000000000000L
-                | (read(stream) << 56);
-    }
-
-    /**
-     * Writes signed short data in <code>srcBuffer</code> to <code>stream</code>
-     * in little endian order.
-     *
-     * @param srcBuffer  Signed short data to be converted. The size of array
-     *                   must be at least <code>srcOffset</code> + <code>srcLength</code>.
-     * @param srcOffset  Offset for reading data from <code>srcBuffer</code>.
-     * @param srcLength  Max. Number of data to be processed from
-     *                   <code>srcBuffer</code>.
-     * @param tempBuffer Temporary buffer to store the converted data bytes in
-     *                   little endian order. The size of the array must be at least 2 and
-     *                   should be a multiple of 2. Use an appropriate size according to
-     *                   <code>srcLength</code>.
-     */
-    public static void writeLittleEndian
-            (
-                    final short[] srcBuffer,
-                    int srcOffset,
-                    int srcLength,
-                    final OutputStream stream,
-                    final byte[] tempBuffer
-            )
-            throws IOException {
-        while (srcLength >= 0) {
-            final int processedData = convertToLittleEndian
-                    (
-                            srcBuffer,
-                            tempBuffer,
-                            srcOffset,
-                            srcLength
-                    );
-            stream.write(tempBuffer, 0, processedData * 2);
-            srcOffset += processedData;
-            srcLength -= processedData;
-        }
-    }
-
-    /**
-     * Writes signed int data in <code>srcBuffer</code> to <code>stream</code>
-     * in little endian order.
-     *
-     * @param srcBuffer  Signed short data to be converted. The size of array
-     *                   must be at least <code>srcOffset</code> + <code>srcLength</code>.
-     * @param srcOffset  Offset for reading data from <code>srcBuffer</code>.
-     * @param srcLength  Max. Number of data to be processed from
-     *                   <code>srcBuffer</code>.
-     * @param tempBuffer Temporary buffer to store the converted data bytes in
-     *                   little endian order. The size of the array must be at least 4 and
-     *                   should be a multiple of 4. Use an appropriate size according to
-     *                   <code>srcLength</code>.
-     */
-    public static void writeLittleEndian
-            (
-                    final int[] srcBuffer,
-                    int srcOffset,
-                    int srcLength,
-                    final OutputStream stream,
-                    final byte[] tempBuffer
-            )
-            throws IOException {
-        while (srcLength > 0) {
-            final int processedData = convertToLittleEndian
-                    (
-                            srcBuffer,
-                            tempBuffer,
-                            srcOffset,
-                            srcLength
-                    );
-            stream.write(tempBuffer, 0, processedData * 4);
-            srcOffset += processedData;
-            srcLength -= processedData;
-        }
-    }
-
-    /**
-     * Writes signed long data in <code>srcBuffer</code> to <code>stream</code>
-     * in little endian order.
-     *
-     * @param srcBuffer  Signed short data to be converted. The size of array
-     *                   must be at least <code>srcOffset</code> + <code>srcLength</code>.
-     * @param srcOffset  Offset for reading data from <code>srcBuffer</code>.
-     * @param srcLength  Max. Number of data to be processed from
-     *                   <code>srcBuffer</code>.
-     * @param tempBuffer Temporary buffer to store the converted data bytes in
-     *                   little endian order. The size of the array must be at least 8 and
-     *                   should be a multiple of 8. Use an appropriate size according to
-     *                   <code>srcLength</code>.
-     */
-    public static void writeLittleEndian
-            (
-                    final long[] srcBuffer,
-                    int srcOffset,
-                    int srcLength,
-                    final OutputStream stream,
-                    final byte[] tempBuffer
-            )
-            throws IOException {
-        while (srcLength > 0) {
-            final int processedData = convertToLittleEndian
-                    (
-                            srcBuffer,
-                            tempBuffer,
-                            srcOffset,
-                            srcLength
-                    );
-            stream.write(tempBuffer, 0, processedData * 8);
-            srcOffset += processedData;
-            srcLength -= processedData;
-        }
-    }
-
-    /**
-     * Writes signed int data in <code>srcBuffer</code> to <code>stream</code>
-     * in little endian order.
-     *
-     * @param srcBuffer  Signed short data to be converted. The size of array
-     *                   must be at least <code>srcOffset</code> + <code>srcLength</code>.
-     * @param srcOffset  Offset for reading data from <code>srcBuffer</code>.
-     * @param srcLength  Max. Number of data to be processed from
-     *                   <code>srcBuffer</code>.
-     * @param tempBuffer Temporary buffer to store the converted data bytes in
-     *                   little endian order. The size of the array must be at least
-     *                   <code>blockSize</code> and should be a multiple of
-     *                   <code>blockSize</code>. Use an appropriate size according to
-     *                   <code>srcLength</code>.
-     */
-    public static void writeLittleEndian
-            (
-                    final int blockSize,
-                    final int[] srcBuffer,
-                    int srcOffset,
-                    int srcLength,
-                    final OutputStream stream,
-                    final byte[] tempBuffer
-            )
-            throws IOException {
-        while (srcLength > 0) {
-            final int processedData = convertToLittleEndian
-                    (
-                            blockSize,
-                            srcBuffer,
-                            tempBuffer,
-                            srcOffset,
-                            srcLength
-                    );
-            stream.write(tempBuffer, 0, processedData * blockSize);
-            srcOffset += processedData;
-            srcLength -= processedData;
-        }
-    }
-
-    /**
-     * Writes float data in <code>srcBuffer</code> to <code>stream</code>
-     * in little endian order. For the conversion the
-     * <code>Float.floatToIntBits</code> method is used.
-     *
-     * @param srcBuffer  Signed short data to be converted. The size of array
-     *                   must be at least <code>srcOffset</code> + <code>srcLength</code>.
-     * @param srcOffset  Offset for reading data from <code>srcBuffer</code>.
-     * @param srcLength  Max. Number of data to be processed from
-     *                   <code>srcBuffer</code>.
-     * @param tempBuffer Temporary buffer to store the converted data bytes in
-     *                   little endian order. The size of the array must be at least 4 and
-     *                   should be a multiple of 4. Use an appropriate size according to
-     *                   <code>srcLength</code>.
-     */
-    public static void writeLittleEndian
-            (
-                    final float[] srcBuffer,
-                    int srcOffset,
-                    int srcLength,
-                    final OutputStream stream,
-                    final byte[] tempBuffer
-            )
-            throws IOException {
-        while (srcLength > 0) {
-            final int processedData = convertToLittleEndian
-                    (
-                            srcBuffer,
-                            tempBuffer,
-                            srcOffset,
-                            srcLength
-                    );
-            stream.write(tempBuffer, 0, processedData * 4);
-            srcOffset += processedData;
-            srcLength -= processedData;
-        }
-    }
-
-    /**
-     * Writes double data in <code>srcBuffer</code> to <code>stream</code>
-     * in little endian order. For the conversion the
-     * <code>Double.doubleToLongBits</code> method is used.
-     *
-     * @param srcBuffer  Signed short data to be converted. The size of array
-     *                   must be at least <code>srcOffset</code> + <code>srcLength</code>.
-     * @param srcOffset  Offset for reading data from <code>srcBuffer</code>.
-     * @param srcLength  Max. Number of data to be processed from
-     *                   <code>srcBuffer</code>.
-     * @param tempBuffer Temporary buffer to store the converted data bytes in
-     *                   little endian order. The size of the array must be at least 8 and
-     *                   should be a multiple of 8. Use an appropriate size according to
-     *                   <code>srcLength</code>.
-     */
-    public static void writeLittleEndian
-            (
-                    final double[] srcBuffer,
-                    int srcOffset,
-                    int srcLength,
-                    final OutputStream stream,
-                    final byte[] tempBuffer
-            )
-            throws IOException {
-        while (srcLength > 0) {
-            final int processedData = convertToLittleEndian
-                    (
-                            srcBuffer,
-                            tempBuffer,
-                            srcOffset,
-                            srcLength
-                    );
-            stream.write(tempBuffer, 0, processedData * 8);
-            srcOffset += processedData;
-            srcLength -= processedData;
-        }
-    }
-
-    /**
-     * Writes signed short data to <code>stream</code> in little endian order.
-     *
-     * @param stream <code>OutputStream</code> to write to.
-     * @param value  Signed short data to be written.
-     */
-    public static void writeLittleEndian
-            (
-                    final OutputStream stream,
-                    final short value
-            )
-            throws IOException {
-        stream.write(value);
-        stream.write(value >> 8);
-    }
-
-    /**
-     * Writes signed int data to <code>stream</code> in little endian order.
-     *
-     * @param stream <code>OutputStream</code> to write to.
-     * @param value  Signed int data to be written.
-     */
-    public static void writeLittleEndian
-            (
-                    final OutputStream stream,
-                    final int value
-            )
-            throws IOException {
-        stream.write(value);
-        stream.write(value >> 8);
-        stream.write(value >> 16);
-        stream.write(value >> 24);
-    }
-
-    /**
-     * Writes signed long data to <code>stream</code> in little endian order.
-     *
-     * @param stream <code>OutputStream</code> to write to.
-     * @param value  Signed long data to be written.
-     */
-    public static void writeLittleEndian
-            (
-                    final OutputStream stream,
-                    long value
-            )
-            throws IOException {
-        for (int i = 0; i < 8; i++) {
-            stream.write((int) value);
-            value >>= 8;
-        }
-    }
-
-    private static int read2ByteBlock
-            (
-                    final byte[] readBuffer,
-                    final int[] destBuffer,
-                    final int destOffset,
-                    final int destLength,
-                    final InputStream stream,
-                    final int mask
-            )
-            throws IOException {
-        int numOfBytesRead = 0;
-        int numOfData = 0;
-        int offset = 0;
-        final int length = (readBuffer.length / 2) * 2;
-        while ((numOfBytesRead >= 0) && (numOfData < destLength)) {
-            // calculate how many more bytes can be read so that destBuffer
-            // does not overflow; enables to continue reading from same stream
-            // without data loss
-            final int maxBytesToRead =
-                    Math.max((destLength - numOfData) * 2, length);
-            numOfBytesRead =
-                    stream.read(readBuffer, offset, maxBytesToRead - offset);
-            int numOfProcessedBytes = convert
-                    (
-                            2,
-                            readBuffer,
-                            destBuffer,
-                            numOfBytesRead + offset,
-                            destOffset + numOfData,
-                            destLength - numOfData,
-                            mask
-                    );
-            // if an uneven number of bytes was read from stream
-            if (numOfBytesRead - numOfProcessedBytes == 1) {
-                offset = 1;
-                readBuffer[0] = readBuffer[numOfProcessedBytes];
-            } else {
-                offset = 0;
-            }
-            numOfData += (numOfProcessedBytes / 2);
-        }
-        return numOfData;
-    }
-
-    private static int read3ByteBlock
-            (
-                    final byte[] readBuffer,
-                    final int[] destBuffer,
-                    final int destOffset,
-                    final int destLength,
-                    final InputStream stream,
-                    final int mask
-            )
-            throws IOException {
-        int numOfBytesRead = 0;
-        int numOfData = 0;
-        int offset = 0;
-        final int length = (readBuffer.length / 3) * 3;
-        while ((numOfBytesRead >= 0) && (numOfData < destLength)) {
-            // calculate how many more bytes can be read so that destBuffer
-            // does not overflow; enables to continue reading from same stream
-            // without data loss
-            final int maxBytesToRead =
-                    Math.max((destLength - numOfData) * 3, length);
-            numOfBytesRead =
-                    stream.read(readBuffer, offset, maxBytesToRead - offset);
-            int numOfProcessedBytes = convert
-                    (
-                            3,
-                            readBuffer,
-                            destBuffer,
-                            numOfBytesRead + offset,
-                            destOffset + numOfData,
-                            destLength - numOfData,
-                            mask
-                    );
-            offset = numOfBytesRead - numOfProcessedBytes;
-            // if a number of bytes was read from stream was not a multiple
-            // of 3
-            if (offset >= 1) {
-                readBuffer[0] = readBuffer[numOfProcessedBytes];
-                if (offset >= 2) {
-                    readBuffer[1] = readBuffer[numOfProcessedBytes + 1];
-                }
-            }
-            numOfData += (numOfProcessedBytes / 3);
-        }
-        return numOfData;
-    }
-
-    private static int read(final InputStream stream) throws IOException {
-        final int tempValue = stream.read();
-        if (tempValue == -1) {
-            throw new IOException("Filesize does not match blocksize");
-        }
-        return tempValue;
-    }
-}
\ No newline at end of file
diff --git a/src/resources/about.properties b/src/resources/about.properties
index d721175..1124c73 100644
--- a/src/resources/about.properties
+++ b/src/resources/about.properties
@@ -1,4 +1,23 @@
-version=1.4.2
-build=5264
-timestamp=05/14/2010 02:15 PM
-master-resource-url=http://www.broadinstitute.org/igv/resources/dataServerRegistry
\ No newline at end of file
+#
+# Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
+# All Rights Reserved.
+#
+# This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
+# is available at http://www.opensource.org/licenses/lgpl-2.1.php.
+#
+# THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
+# ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
+# OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
+# RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
+# ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
+# DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
+# BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
+# FOREGOING.
+#
+
+version=1.5.56
+build=6203
+timestamp=03/10/2011 12:32 AM
+master-resource-url=@DEFAULT_MASTER_RESOURCE_URL
+master-genome-url=@DEFAULT_MASTER_GENOME_URL
\ No newline at end of file

-- 
Integrative Genomics Viewer



More information about the debian-med-commit mailing list